Add 1.21.2 & 1.21.0 support

This commit is contained in:
AkmalFairuz
2025-01-01 13:40:15 +07:00
parent 123f125d4e
commit f6548dd6a7
29 changed files with 7641 additions and 7 deletions

View File

@@ -9,8 +9,16 @@ A gophertunnel protocol interface implementation to support older Minecraft Bedr
| 748 | 1.21.40 | ✅ |
| 729 | 1.21.30 | ✅ |
| 712 | 1.21.20 | ✅ |
| 686 | 1.21.2 | 🚧 |
| 685 | 1.21.0 | 🚧 |
| 686 | 1.21.2 | |
| 685 | 1.21.0 | |
| 671 | 1.20.80 | 🚧 |
| 662 | 1.20.70 | 🚧 |
| 649 | 1.20.60 | 🚧 |
| 630 | 1.20.50 | 🚧 |
| 622 | 1.20.40 | 🚧 |
| 618 | 1.20.30 | 🚧 |
| 594 | 1.20.10 | 🚧 |
| 589 | 1.20.0 | 🚧 |
## Credits
- [Flonja/multiversion](https://github.com/Flonja/multiversion)

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,76 @@
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"
)
// AddActor is sent by the server to the client to spawn an entity to the player. It is used for every entity
// except other players, for which the AddPlayer packet is used.
type AddActor struct {
// EntityUniqueID is the unique ID of the entity. The unique ID is a value that remains consistent across
// different sessions of the same world, but most servers simply fill the runtime ID of the entity out for
// this field.
EntityUniqueID int64
// 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
// EntityType is the string entity type of the entity, for example 'minecraft:skeleton'. A list of these
// entities may be found online.
EntityType string
// Position is the position to spawn the entity on. If the entity is on a distance that the player cannot
// see it, the entity will still show up if the player moves closer.
Position mgl32.Vec3
// Velocity is the initial velocity the entity spawns with. This velocity will initiate client side
// movement of the entity.
Velocity mgl32.Vec3
// Pitch is the vertical rotation of the entity. Facing straight forward yields a pitch of 0. Pitch is
// measured in degrees.
Pitch float32
// Yaw is the horizontal rotation of the entity. Yaw is also measured in degrees.
Yaw float32
// HeadYaw is the same as Yaw, except that it applies specifically to the head of the entity. A different value for
// HeadYaw than Yaw means that the entity will have its head turned.
HeadYaw float32
// BodyYaw is the same as Yaw, except that it applies specifically to the body of the entity. A different value for
// BodyYaw than HeadYaw means that the entity will have its body turned, although it is unclear what the difference
// between BodyYaw and Yaw is.
BodyYaw float32
// Attributes is a slice of attributes that the entity has. It includes attributes such as its health,
// movement speed, etc.
Attributes []protocol.AttributeValue
// EntityMetadata is a map of entity metadata, which includes flags and data properties that alter in
// particular the way the entity looks. Flags include ones such as 'on fire' and 'sprinting'.
// The metadata values are indexed by their property key.
EntityMetadata map[uint32]any
// EntityProperties is a list of properties that the entity inhibits. These properties define and alter specific
// attributes of the entity.
EntityProperties protocol.EntityProperties
// EntityLinks is a list of entity links that are currently active on the entity. These links alter the
// way the entity shows up when first spawned in terms of it shown as riding an entity. Setting these
// links is important for new viewers to see the entity is riding another entity.
EntityLinks []proto.EntityLink
}
// ID ...
func (*AddActor) ID() uint32 {
return packet.IDAddActor
}
func (pk *AddActor) Marshal(io protocol.IO) {
io.Varint64(&pk.EntityUniqueID)
io.Varuint64(&pk.EntityRuntimeID)
io.String(&pk.EntityType)
io.Vec3(&pk.Position)
io.Vec3(&pk.Velocity)
io.Float32(&pk.Pitch)
io.Float32(&pk.Yaw)
io.Float32(&pk.HeadYaw)
io.Float32(&pk.BodyYaw)
protocol.Slice(io, &pk.Attributes)
io.EntityMetadata(&pk.EntityMetadata)
protocol.Single(io, &pk.EntityProperties)
protocol.Slice(io, &pk.EntityLinks)
}

View File

@@ -0,0 +1,92 @@
package legacypacket
import (
"github.com/akmalfairuz/legacy-version/legacyver/proto"
"github.com/go-gl/mathgl/mgl32"
"github.com/google/uuid"
"github.com/sandertv/gophertunnel/minecraft/protocol"
"github.com/sandertv/gophertunnel/minecraft/protocol/packet"
)
// AddPlayer is sent by the server to the client to make a player entity show up client-side. It is one of the
// few entities that cannot be sent using the AddActor packet.
type AddPlayer struct {
// UUID is the UUID of the player. It is the same UUID that the client sent in the Login packet at the
// start of the session. A player with this UUID must exist in the player list (built up using the
// PlayerList packet), for it to show up in-game.
UUID uuid.UUID
// Username is the name of the player. This username is the username that will be set as the initial
// name tag of the player.
Username string
// EntityRuntimeID is the runtime ID of the player. The runtime ID is unique for each world session, and
// entities are generally identified in packets using this runtime ID.
EntityRuntimeID uint64
// PlatformChatID is an identifier only set for particular platforms when chatting (presumably only for
// Nintendo Switch). It is otherwise an empty string, and is used to decide which players are able to
// chat with each other.
PlatformChatID string
// Position is the position to spawn the player on. If the player is on a distance that the viewer cannot
// see it, the player will still show up if the viewer moves closer.
Position mgl32.Vec3
// Velocity is the initial velocity the player spawns with. This velocity will initiate client side
// movement of the player.
Velocity mgl32.Vec3
// Pitch is the vertical rotation of the player. Facing straight forward yields a pitch of 0. Pitch is
// measured in degrees.
Pitch float32
// Yaw is the horizontal rotation of the player. Yaw is also measured in degrees.
Yaw float32
// HeadYaw is the same as Yaw, except that it applies specifically to the head of the player. A different
// value for HeadYaw than Yaw means that the player will have its head turned.
HeadYaw float32
// HeldItem is the item that the player is holding. The item is shown to the viewer as soon as the player
// itself shows up. Needless to say that this field is rather pointless, as additional packets still must
// be sent for armour to show up.
HeldItem protocol.ItemInstance
// GameType is the game type of the player. If set to GameTypeSpectator, the player will not be shown to viewers.
GameType int32
// EntityMetadata is a map of entity metadata, which includes flags and data properties that alter in
// particular the way the player looks. Flags include ones such as 'on fire' and 'sprinting'.
// The metadata values are indexed by their property key.
EntityMetadata map[uint32]any
// EntityProperties is a list of properties that the entity inhibits. These properties define and alter specific
// attributes of the entity.
EntityProperties protocol.EntityProperties
// AbilityData represents various data about the abilities of a player, such as ability layers or permissions.
AbilityData protocol.AbilityData
// EntityLinks is a list of entity links that are currently active on the player. These links alter the
// way the player shows up when first spawned in terms of it shown as riding an entity. Setting these
// links is important for new viewers to see the player is riding another entity.
EntityLinks []proto.EntityLink
// DeviceID is the device ID set in one of the files found in the storage of the device of the player. It
// may be changed freely, so it should not be relied on for anything.
DeviceID string
// BuildPlatform is the build platform/device OS of the player that is about to be added, as it sent in
// the Login packet when joining.
BuildPlatform int32
}
// ID ...
func (*AddPlayer) ID() uint32 {
return packet.IDAddPlayer
}
func (pk *AddPlayer) Marshal(io protocol.IO) {
io.UUID(&pk.UUID)
io.String(&pk.Username)
io.Varuint64(&pk.EntityRuntimeID)
io.String(&pk.PlatformChatID)
io.Vec3(&pk.Position)
io.Vec3(&pk.Velocity)
io.Float32(&pk.Pitch)
io.Float32(&pk.Yaw)
io.Float32(&pk.HeadYaw)
io.ItemInstance(&pk.HeldItem)
io.Varint32(&pk.GameType)
io.EntityMetadata(&pk.EntityMetadata)
protocol.Single(io, &pk.EntityProperties)
protocol.Single(io, &pk.AbilityData)
protocol.Slice(io, &pk.EntityLinks)
io.String(&pk.DeviceID)
io.Int32(&pk.BuildPlatform)
}

View File

@@ -0,0 +1,36 @@
package legacypacket
import (
"github.com/akmalfairuz/legacy-version/legacyver/proto"
"github.com/sandertv/gophertunnel/minecraft/protocol"
"github.com/sandertv/gophertunnel/minecraft/protocol/packet"
)
// CameraInstruction gives a custom camera specific instructions to operate.
type CameraInstruction struct {
// Set is a camera instruction that sets the camera to a specified preset.
Set protocol.Optional[protocol.CameraInstructionSet]
// Clear can be set to true to clear all the current camera instructions.
Clear protocol.Optional[bool]
// Fade is a camera instruction that fades the screen to a specified colour.
Fade protocol.Optional[protocol.CameraInstructionFade]
// Target is a camera instruction that targets a specific entity.
Target protocol.Optional[protocol.CameraInstructionTarget]
// RemoveTarget can be set to true to remove the current aim assist target.
RemoveTarget protocol.Optional[bool]
}
// ID ...
func (*CameraInstruction) ID() uint32 {
return packet.IDCameraInstruction
}
func (pk *CameraInstruction) Marshal(io protocol.IO) {
protocol.OptionalMarshaler(io, &pk.Set)
protocol.OptionalFunc(io, &pk.Clear, io.Bool)
protocol.OptionalMarshaler(io, &pk.Fade)
if proto.IsProtoGTE(io, proto.ID712) {
protocol.OptionalMarshaler(io, &pk.Target)
protocol.OptionalFunc(io, &pk.RemoveTarget, io.Bool)
}
}

View File

@@ -0,0 +1,46 @@
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"
)
// ChangeDimension is sent by the server to the client to send a dimension change screen client-side. Once the
// screen is cleared client-side, the client will send a PlayerAction packet with
// PlayerActionDimensionChangeDone.
type ChangeDimension struct {
// Dimension is the dimension that the client should be changed to. The fog colour will change depending
// on the type of dimension, which is one of the constants above.
// Note that Dimension MUST be a different dimension than the one that the player is currently in. Sending
// a ChangeDimension packet with a Dimension that the player is currently in will result in a never-ending
// dimension change screen.
Dimension int32
// Position is the position in the new dimension that the player is spawned in.
Position mgl32.Vec3
// Respawn specifies if the dimension change was respawn based, meaning that the player died in one
// dimension and got respawned into another. The client will send a PlayerAction packet with
// PlayerActionDimensionChangeRequest if it dies in another dimension, indicating that it needs a
// DimensionChange packet with Respawn set to true.
Respawn bool
// LoadingScreenID is a unique ID for the loading screen that the player is currently in. The client will
// update the server on its state through the ServerBoundLoadingScreen packet, and it can be used to not
// send specific packets to the client if it is changing dimensions. This field should be unique for every
//ChangeDimension packet sent.
LoadingScreenID protocol.Optional[uint32]
}
// ID ...
func (*ChangeDimension) ID() uint32 {
return packet.IDChangeDimension
}
func (pk *ChangeDimension) Marshal(io protocol.IO) {
io.Varint32(&pk.Dimension)
io.Vec3(&pk.Position)
io.Bool(&pk.Respawn)
if proto.IsProtoGTE(io, proto.ID712) {
protocol.OptionalFunc(io, &pk.LoadingScreenID, io.Uint32)
}
}

View File

@@ -0,0 +1,50 @@
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"
)
// CorrectPlayerMovePrediction is sent by the server if and only if StartGame.ServerAuthoritativeMovementMode
// is set to AuthoritativeMovementModeServerWithRewind. The packet is used to correct movement at a specific
// point in time.
type CorrectPlayerMovePrediction struct {
// PredictionType is the type of prediction that was corrected. It is one of the constants above.
PredictionType byte
// Position is the position that the player is supposed to be at the tick written in the field below.
// The client will change its current position based on movement after that tick starting from the
// Position.
Position mgl32.Vec3
// Delta is the change in position compared to what the client sent as its position at that specific tick.
Delta mgl32.Vec3
// Rotation is the rotation of the player at the tick written in the field below. It is only included if
// PredictionType is PredictionTypeVehicle.
Rotation mgl32.Vec2
// VehicleAngularVelocity is the angular velocity of the vehicle that the rider is riding.
VehicleAngularVelocity protocol.Optional[float32]
// OnGround specifies if the player was on the ground at the time of the tick below.
OnGround bool
// Tick is the tick of the movement which was corrected by this packet.
Tick uint64
}
// ID ...
func (*CorrectPlayerMovePrediction) ID() uint32 {
return packet.IDCorrectPlayerMovePrediction
}
func (pk *CorrectPlayerMovePrediction) Marshal(io protocol.IO) {
io.Uint8(&pk.PredictionType)
io.Vec3(&pk.Position)
io.Vec3(&pk.Delta)
if pk.PredictionType == packet.PredictionTypeVehicle {
io.Vec2(&pk.Rotation)
if proto.IsProtoGTE(io, proto.ID712) {
protocol.OptionalFunc(io, &pk.VehicleAngularVelocity, io.Float32)
}
}
io.Bool(&pk.OnGround)
io.Varuint64(&pk.Tick)
}

View File

@@ -0,0 +1,40 @@
package legacypacket
import (
"github.com/akmalfairuz/legacy-version/legacyver/proto"
"github.com/sandertv/gophertunnel/minecraft/protocol"
"github.com/sandertv/gophertunnel/minecraft/protocol/packet"
)
// Disconnect may be sent by the server to disconnect the client using an optional message to send as the
// disconnect screen.
type Disconnect struct {
// Reason is the reason for the disconnection. It seems as if this field has no use other than for
// telemetry reasons as it does not affect the message that gets displayed on the disconnect screen.
Reason int32
// HideDisconnectionScreen specifies if the disconnection screen should be hidden when the client is
// disconnected, meaning it will be sent directly to the main menu.
HideDisconnectionScreen bool
// Message is an optional message to show when disconnected. This message is only written if the
// HideDisconnectionScreen field is set to true.
Message string
// FilteredMessage is a filtered version of Message with all the profanity removed. The client will use
// this over Message if this field is not empty and they have the "Filter Profanity" setting enabled.
FilteredMessage string
}
// ID ...
func (*Disconnect) ID() uint32 {
return packet.IDDisconnect
}
func (pk *Disconnect) Marshal(io protocol.IO) {
io.Varint32(&pk.Reason)
io.Bool(&pk.HideDisconnectionScreen)
if !pk.HideDisconnectionScreen {
io.String(&pk.Message)
if proto.IsProtoGTE(io, proto.ID712) {
io.String(&pk.FilteredMessage)
}
}
}

View File

@@ -0,0 +1,29 @@
package legacypacket
import (
"github.com/akmalfairuz/legacy-version/legacyver/proto"
"github.com/sandertv/gophertunnel/minecraft/nbt"
"github.com/sandertv/gophertunnel/minecraft/protocol"
"github.com/sandertv/gophertunnel/minecraft/protocol/packet"
)
// EditorNetwork is a packet sent from the server to the client and vise-versa to communicate editor-mode related
// information. It carries a single compound tag containing the relevant information.
type EditorNetwork struct {
// RouteToManager ...
RouteToManager bool
// Payload is a network little endian compound tag holding data relevant to the editor.
Payload map[string]any
}
// ID ...
func (*EditorNetwork) ID() uint32 {
return packet.IDEditorNetwork
}
func (pk *EditorNetwork) Marshal(io protocol.IO) {
if proto.IsProtoGTE(io, proto.ID712) {
io.Bool(&pk.RouteToManager)
}
io.NBT(&pk.Payload, nbt.NetworkLittleEndian)
}

View File

@@ -40,6 +40,8 @@ func (pk *InventoryContent) Marshal(io protocol.IO) {
if proto.IsProtoGTE(io, proto.ID748) {
io.ItemInstance(&pk.StorageItem)
} else {
if proto.IsProtoGTE(io, proto.ID712) {
io.Varuint32(&pk.DynamicContainerSize)
}
}
}

View File

@@ -43,7 +43,9 @@ func (pk *InventorySlot) Marshal(io protocol.IO) {
if proto.IsProtoGTE(io, proto.ID748) {
io.ItemInstance(&pk.StorageItem)
} else {
if proto.IsProtoGTE(io, proto.ID712) {
io.Varuint32(&pk.DynamicContainerSize)
}
}
io.ItemInstance(&pk.NewItem)
}

View File

@@ -0,0 +1,50 @@
package legacypacket
import (
"github.com/akmalfairuz/legacy-version/legacyver/proto"
"github.com/sandertv/gophertunnel/minecraft/protocol"
"github.com/sandertv/gophertunnel/minecraft/protocol/packet"
)
// InventoryTransaction is a packet sent by the client. It essentially exists out of multiple sub-packets,
// each of which have something to do with the inventory in one way or another. Some of these sub-packets
// directly relate to the inventory, others relate to interaction with the world, that could potentially
// result in a change in the inventory.
type InventoryTransaction struct {
// LegacyRequestID is an ID that is only non-zero at times when sent by the client. The server should
// always send 0 for this. When this field is not 0, the LegacySetItemSlots slice below will have values
// in it.
// LegacyRequestID ties in with the ItemStackResponse packet. If this field is non-0, the server should
// respond with an ItemStackResponse packet. Some inventory actions such as dropping an item out of the
// hotbar are still one using this packet, and the ItemStackResponse packet needs to tie in with it.
LegacyRequestID int32
// LegacySetItemSlots are only present if the LegacyRequestID is non-zero. These item slots inform the
// server of the slots that were changed during the inventory transaction, and the server should send
// back an ItemStackResponse packet with these slots present in it. (Or false with no slots, if rejected.)
LegacySetItemSlots []protocol.LegacySetItemSlot
// Actions is a list of actions that took place, that form the inventory transaction together. Each of
// these actions hold one slot in which one item was changed to another. In general, the combination of
// all of these actions results in a balanced inventory transaction. This should be checked to ensure that
// no items are cheated into the inventory.
Actions []protocol.InventoryAction
// TransactionData is a data object that holds data specific to the type of transaction that the
// TransactionPacket held. Its concrete type must be one of NormalTransactionData, MismatchTransactionData
// UseItemTransactionData, UseItemOnEntityTransactionData or ReleaseItemTransactionData. If nil is set,
// the transaction will be assumed to of type InventoryTransactionTypeNormal.
TransactionData proto.InventoryTransactionData
}
// ID ...
func (*InventoryTransaction) ID() uint32 {
return packet.IDInventoryTransaction
}
func (pk *InventoryTransaction) Marshal(io protocol.IO) {
io.Varint32(&pk.LegacyRequestID)
if pk.LegacyRequestID != 0 {
protocol.Slice(io, &pk.LegacySetItemSlots)
}
proto.TransactionDataType(io, &pk.TransactionData)
protocol.Slice(io, &pk.Actions)
pk.TransactionData.Marshal(io)
}

View File

@@ -0,0 +1,44 @@
package legacypacket
import (
"github.com/akmalfairuz/legacy-version/legacyver/proto"
"github.com/sandertv/gophertunnel/minecraft/protocol"
"github.com/sandertv/gophertunnel/minecraft/protocol/packet"
)
// MobArmourEquipment is sent by the server to the client to update the armour an entity is wearing. It is
// sent for both players and other entities, such as zombies.
type MobArmourEquipment 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
// Helmet is the equipped helmet of the entity. Items that are not wearable on the head will not be
// rendered by the client. Unlike in Java Edition, blocks cannot be worn.
Helmet protocol.ItemInstance
// Chestplate is the chestplate of the entity. Items that are not wearable as chestplate will not be
// rendered.
Chestplate protocol.ItemInstance
// Leggings is the item worn as leggings by the entity. Items not wearable as leggings will not be
// rendered client-side.
Leggings protocol.ItemInstance
// Boots is the item worn as boots by the entity. Items not wearable as boots will not be rendered.
Boots protocol.ItemInstance
// Body is the item worn on the body of the entity. Items not wearable on the body will not be rendered.
Body protocol.ItemInstance
}
// ID ...
func (*MobArmourEquipment) ID() uint32 {
return packet.IDMobArmourEquipment
}
func (pk *MobArmourEquipment) Marshal(io protocol.IO) {
io.Varuint64(&pk.EntityRuntimeID)
io.ItemInstance(&pk.Helmet)
io.ItemInstance(&pk.Chestplate)
io.ItemInstance(&pk.Leggings)
io.ItemInstance(&pk.Boots)
if proto.IsProtoGTE(io, proto.ID712) {
io.ItemInstance(&pk.Body)
}
}

View File

@@ -0,0 +1,63 @@
package legacypacket
import (
"github.com/akmalfairuz/legacy-version/legacyver/proto"
"github.com/sandertv/gophertunnel/minecraft/protocol"
"github.com/sandertv/gophertunnel/minecraft/protocol/packet"
)
// PlayerArmourDamage is sent by the server to damage the armour of a player. It is a very efficient packet,
// but generally it's much easier to just send a slot update for the damaged armour.
type PlayerArmourDamage struct {
// Bitset holds a bitset of 4 bits that indicate which pieces of armour need to have damage dealt to them.
// The first bit, when toggled, is for a helmet, the second for the chestplate, the third for the leggings
// and the fourth for boots.
Bitset uint8
// HelmetDamage is the amount of damage that should be dealt to the helmet.
HelmetDamage int32
// ChestplateDamage is the amount of damage that should be dealt to the chestplate.
ChestplateDamage int32
// LeggingsDamage is the amount of damage that should be dealt to the leggings.
LeggingsDamage int32
// BootsDamage is the amount of damage that should be dealt to the boots.
BootsDamage int32
// BodyDamage is the amount of damage that should be dealt to the body.
BodyDamage int32
}
// ID ...
func (pk *PlayerArmourDamage) ID() uint32 {
return packet.IDPlayerArmourDamage
}
func (pk *PlayerArmourDamage) Marshal(io protocol.IO) {
io.Uint8(&pk.Bitset)
if pk.Bitset&packet.PlayerArmourDamageFlagHelmet != 0 {
io.Varint32(&pk.HelmetDamage)
} else {
pk.HelmetDamage = 0
}
if pk.Bitset&packet.PlayerArmourDamageFlagChestplate != 0 {
io.Varint32(&pk.ChestplateDamage)
} else {
pk.ChestplateDamage = 0
}
if pk.Bitset&packet.PlayerArmourDamageFlagLeggings != 0 {
io.Varint32(&pk.LeggingsDamage)
} else {
pk.LeggingsDamage = 0
}
if pk.Bitset&packet.PlayerArmourDamageFlagBoots != 0 {
io.Varint32(&pk.BootsDamage)
} else {
pk.BootsDamage = 0
}
if proto.IsProtoGTE(io, proto.ID712) {
if pk.Bitset&packet.PlayerArmourDamageFlagBody != 0 {
io.Varint32(&pk.BodyDamage)
} else {
pk.BodyDamage = 0
}
}
}

View File

@@ -91,7 +91,7 @@ func (pk *PlayerAuthInput) Marshal(io protocol.IO) {
io.Vec3(&pk.Delta)
if pk.InputData.Load(packet.InputFlagPerformItemInteraction) {
io.PlayerInventoryAction(&pk.ItemInteractionData)
proto.PlayerInventoryAction(io, &pk.ItemInteractionData)
}
if pk.InputData.Load(packet.InputFlagPerformItemStackRequest) {

View File

@@ -0,0 +1,25 @@
package legacypacket
import (
"github.com/akmalfairuz/legacy-version/legacyver/proto"
"github.com/sandertv/gophertunnel/minecraft/protocol"
"github.com/sandertv/gophertunnel/minecraft/protocol/packet"
)
// SetActorLink is sent by the server to initiate an entity link client-side, meaning one entity will start
// riding another.
type SetActorLink struct {
// EntityLink is the link to be set client-side. It links two entities together, so that one entity rides
// another. Note that players that see those entities later will not see the link, unless it is also sent
// in the AddActor and AddPlayer packets.
EntityLink proto.EntityLink
}
// ID ...
func (*SetActorLink) ID() uint32 {
return packet.IDSetActorLink
}
func (pk *SetActorLink) Marshal(io protocol.IO) {
protocol.Single(io, &pk.EntityLink)
}

View File

@@ -0,0 +1,53 @@
package legacypacket
import (
"github.com/akmalfairuz/legacy-version/legacyver/proto"
"github.com/sandertv/gophertunnel/minecraft/protocol"
"github.com/sandertv/gophertunnel/minecraft/protocol/packet"
)
// SetTitle is sent by the server to make a title, subtitle or action bar shown to a player. It has several
// fields that allow setting the duration of the titles.
type SetTitle struct {
// ActionType is the type of the action that should be executed upon the title of a player. It is one of
// the constants above and specifies the response of the client to the packet.
ActionType int32
// Text is the text of the title, which has a different meaning depending on the ActionType that the
// packet has. The text is the text of a title, subtitle or action bar, depending on the type set.
Text string
// FadeInDuration is the duration that the title takes to fade in on the screen of the player. It is
// measured in 20ths of a second (AKA in ticks).
FadeInDuration int32
// RemainDuration is the duration that the title remains on the screen of the player. It is measured in
// 20ths of a second (AKA in ticks).
RemainDuration int32
// FadeOutDuration is the duration that the title takes to fade out of the screen of the player. It is
// measured in 20ths of a second (AKA in ticks).
FadeOutDuration int32
// XUID is the XBOX Live user ID of the player, which will remain consistent as long as the player is
// logged in with the XBOX Live account. It is empty if the user is not logged into its XBL account.
XUID string
// PlatformOnlineID is either a uint64 or an empty string.
PlatformOnlineID string
// FilteredMessage is a filtered version of Message with all the profanity removed. The client will use
// this over Message if this field is not empty and they have the "Filter Profanity" setting enabled.
FilteredMessage string
}
// ID ...
func (*SetTitle) ID() uint32 {
return packet.IDSetTitle
}
func (pk *SetTitle) Marshal(io protocol.IO) {
io.Varint32(&pk.ActionType)
io.String(&pk.Text)
io.Varint32(&pk.FadeInDuration)
io.Varint32(&pk.RemainDuration)
io.Varint32(&pk.FadeOutDuration)
io.String(&pk.XUID)
io.String(&pk.PlatformOnlineID)
if proto.IsProtoGTE(io, proto.ID712) {
io.String(&pk.FilteredMessage)
}
}

View File

@@ -0,0 +1,33 @@
package legacypacket
import (
"github.com/akmalfairuz/legacy-version/legacyver/proto"
"github.com/sandertv/gophertunnel/minecraft/protocol"
"github.com/sandertv/gophertunnel/minecraft/protocol/packet"
)
// StopSound is sent by the server to stop a sound playing to the player, such as a playing music disk track
// or other long-lasting sounds.
type StopSound struct {
// SoundName is the name of the sound that should be stopped from playing. If no sound with this name is
// currently active, the packet is ignored.
SoundName string
// StopAll specifies if all sounds currently playing to the player should be stopped. If set to true, the
// SoundName field may be left empty.
StopAll bool
// StopMusicLegacy is currently unknown.
StopMusicLegacy bool
}
// ID ...
func (*StopSound) ID() uint32 {
return packet.IDStopSound
}
func (pk *StopSound) Marshal(io protocol.IO) {
io.String(&pk.SoundName)
io.Bool(&pk.StopAll)
if proto.IsProtoGTE(io, proto.ID712) {
io.Bool(&pk.StopMusicLegacy)
}
}

View File

@@ -0,0 +1,56 @@
package proto
import "github.com/sandertv/gophertunnel/minecraft/protocol"
// EntityLink is a link between two entities, typically being one entity riding another.
type EntityLink struct {
// RiddenEntityUniqueID is the entity unique ID of the entity that is being ridden. For a player sitting
// in a boat, this is the unique ID of the boat.
RiddenEntityUniqueID int64
// RiderEntityUniqueID is the entity unique ID of the entity that is riding. For a player sitting in a
// boat, this is the unique ID of the player.
RiderEntityUniqueID int64
// Type is one of the types above. It specifies the way the entity is linked to another entity.
Type byte
// Immediate is set to immediately dismount an entity from another. This should be set when the mount of
// an entity is killed.
Immediate bool
// RiderInitiated specifies if the link was created by the rider, for example the player starting to ride
// a horse by itself. This is generally true in vanilla environment for players.
RiderInitiated bool
// VehicleAngularVelocity is the angular velocity of the vehicle that the rider is riding.
VehicleAngularVelocity float32
}
func (x *EntityLink) FromLatest(link protocol.EntityLink) EntityLink {
x.RiddenEntityUniqueID = link.RiddenEntityUniqueID
x.RiderEntityUniqueID = link.RiderEntityUniqueID
x.Type = link.Type
x.Immediate = link.Immediate
x.RiderInitiated = link.RiderInitiated
x.VehicleAngularVelocity = link.VehicleAngularVelocity
return *x
}
func (x *EntityLink) ToLatest() protocol.EntityLink {
return protocol.EntityLink{
RiddenEntityUniqueID: x.RiddenEntityUniqueID,
RiderEntityUniqueID: x.RiderEntityUniqueID,
Type: x.Type,
Immediate: x.Immediate,
RiderInitiated: x.RiderInitiated,
VehicleAngularVelocity: x.VehicleAngularVelocity,
}
}
// Marshal encodes/decodes a single entity link.
func (x *EntityLink) Marshal(r protocol.IO) {
r.Varint64(&x.RiddenEntityUniqueID)
r.Varint64(&x.RiderEntityUniqueID)
r.Uint8(&x.Type)
r.Bool(&x.Immediate)
r.Bool(&x.RiderInitiated)
if IsProtoGTE(r, ID712) {
r.Float32(&x.VehicleAngularVelocity)
}
}

View File

@@ -0,0 +1,158 @@
package proto
import (
"github.com/go-gl/mathgl/mgl32"
"github.com/sandertv/gophertunnel/minecraft/protocol"
)
// InventoryTransactionData represents an object that holds data specific to an inventory transaction type.
// The data it holds depends on the type.
type InventoryTransactionData interface {
// Marshal encodes/decodes a serialised inventory transaction data object.
Marshal(r protocol.IO)
}
// lookupTransactionData looks up inventory transaction data for the ID passed.
func lookupTransactionData(id uint32, x *InventoryTransactionData) bool {
switch id {
case protocol.InventoryTransactionTypeNormal:
*x = &protocol.NormalTransactionData{}
case protocol.InventoryTransactionTypeMismatch:
*x = &protocol.MismatchTransactionData{}
case protocol.InventoryTransactionTypeUseItem:
*x = &UseItemTransactionData{}
case protocol.InventoryTransactionTypeUseItemOnEntity:
*x = &protocol.UseItemOnEntityTransactionData{}
case protocol.InventoryTransactionTypeReleaseItem:
*x = &protocol.ReleaseItemTransactionData{}
default:
return false
}
return true
}
// lookupTransactionDataType looks up an ID for a specific transaction data.
func lookupTransactionDataType(x InventoryTransactionData, id *uint32) bool {
switch x.(type) {
case *protocol.NormalTransactionData:
*id = protocol.InventoryTransactionTypeNormal
case *protocol.MismatchTransactionData:
*id = protocol.InventoryTransactionTypeMismatch
case *UseItemTransactionData:
*id = protocol.InventoryTransactionTypeUseItem
case *protocol.UseItemOnEntityTransactionData:
*id = protocol.InventoryTransactionTypeUseItemOnEntity
case *protocol.ReleaseItemTransactionData:
*id = protocol.InventoryTransactionTypeReleaseItem
default:
return false
}
return true
}
// UseItemTransactionData represents an inventory transaction data object sent when the client uses an item on
// a block.
type UseItemTransactionData struct {
// LegacyRequestID is an ID that is only non-zero at times when sent by the client. The server should
// always send 0 for this. When this field is not 0, the LegacySetItemSlots slice below will have values
// in it.
// LegacyRequestID ties in with the ItemStackResponse packet. If this field is non-0, the server should
// respond with an ItemStackResponse packet. Some inventory actions such as dropping an item out of the
// hotbar are still one using this packet, and the ItemStackResponse packet needs to tie in with it.
LegacyRequestID int32
// LegacySetItemSlots are only present if the LegacyRequestID is non-zero. These item slots inform the
// server of the slots that were changed during the inventory transaction, and the server should send
// back an ItemStackResponse packet with these slots present in it. (Or false with no slots, if rejected.)
LegacySetItemSlots []protocol.LegacySetItemSlot
// Actions is a list of actions that took place, that form the inventory transaction together. Each of
// these actions hold one slot in which one item was changed to another. In general, the combination of
// all of these actions results in a balanced inventory transaction. This should be checked to ensure that
// no items are cheated into the inventory.
Actions []protocol.InventoryAction
// ActionType is the type of the UseItem inventory transaction. It is one of the action types found above,
// and specifies the way the player interacted with the block.
ActionType uint32
// TriggerType is the type of the trigger that caused the inventory transaction. It is one of the trigger
// types found in the constants above. If TriggerType is TriggerTypePlayerInput, the transaction is from
// the initial input of the player. If it is TriggerTypeSimulationTick, the transaction is from a simulation
// tick when the player is holding down the input.
TriggerType uint32
// BlockPosition is the position of the block that was interacted with. This is only really a correct
// block position if ActionType is not UseItemActionClickAir.
BlockPosition protocol.BlockPos
// BlockFace is the face of the block that was interacted with. When clicking the block, it is the face
// clicked. When breaking the block, it is the face that was last being hit until the block broke.
BlockFace int32
// HotBarSlot is the hot bar slot that the player was holding while clicking the block. It should be used
// to ensure that the hot bar slot and held item are correctly synchronised with the server.
HotBarSlot int32
// HeldItem is the item that was held to interact with the block. The server should check if this item
// is actually present in the HotBarSlot.
HeldItem protocol.ItemInstance
// Position is the position of the player at the time of interaction. For clicking a block, this is the
// position at that time, whereas for breaking the block it is the position at the time of breaking.
Position mgl32.Vec3
// ClickedPosition is the position that was clicked relative to the block's base coordinate. It can be
// used to find out exactly where a player clicked the block.
ClickedPosition mgl32.Vec3
// BlockRuntimeID is the runtime ID of the block that was clicked. It may be used by the server to verify
// that the player's world client-side is synchronised with the server's.
BlockRuntimeID uint32
// ClientPrediction is the client's prediction on the output of the transaction. It is one of the client
// prediction found in the constants above.
ClientPrediction uint32
}
func (x *UseItemTransactionData) FromLatest(l *protocol.UseItemTransactionData) *UseItemTransactionData {
return &UseItemTransactionData{
LegacyRequestID: l.LegacyRequestID,
LegacySetItemSlots: l.LegacySetItemSlots,
Actions: l.Actions,
ActionType: l.ActionType,
TriggerType: l.TriggerType,
BlockPosition: l.BlockPosition,
BlockFace: l.BlockFace,
HotBarSlot: l.HotBarSlot,
HeldItem: l.HeldItem,
Position: l.Position,
ClickedPosition: l.ClickedPosition,
BlockRuntimeID: l.BlockRuntimeID,
ClientPrediction: l.ClientPrediction,
}
}
func (x *UseItemTransactionData) ToLatest() *protocol.UseItemTransactionData {
return &protocol.UseItemTransactionData{
LegacyRequestID: x.LegacyRequestID,
LegacySetItemSlots: x.LegacySetItemSlots,
Actions: x.Actions,
ActionType: x.ActionType,
TriggerType: x.TriggerType,
BlockPosition: x.BlockPosition,
BlockFace: x.BlockFace,
HotBarSlot: x.HotBarSlot,
HeldItem: x.HeldItem,
Position: x.Position,
ClickedPosition: x.ClickedPosition,
BlockRuntimeID: x.BlockRuntimeID,
ClientPrediction: x.ClientPrediction,
}
}
// Marshal ...
func (x *UseItemTransactionData) Marshal(r protocol.IO) {
r.Varuint32(&x.ActionType)
if IsProtoGTE(r, ID712) {
r.Varuint32(&x.TriggerType)
}
r.UBlockPos(&x.BlockPosition)
r.Varint32(&x.BlockFace)
r.Varint32(&x.HotBarSlot)
r.ItemInstance(&x.HeldItem)
r.Vec3(&x.Position)
r.Vec3(&x.ClickedPosition)
r.Varuint32(&x.BlockRuntimeID)
if IsProtoGTE(r, ID712) {
r.Varuint32(&x.ClientPrediction)
}
}

View File

@@ -1,6 +1,7 @@
package proto
import (
"fmt"
"github.com/sandertv/gophertunnel/minecraft/protocol"
)
@@ -57,3 +58,41 @@ func EmptySlice[T any](io protocol.IO, slice *[]T) {
*slice = make([]T, 0)
}
}
func TransactionDataType(io protocol.IO, x *InventoryTransactionData) {
if IsReader(io) {
var transactionType uint32
io.Varuint32(&transactionType)
if !lookupTransactionData(transactionType, x) {
io.UnknownEnumOption(transactionType, "inventory transaction data type")
}
} else {
var id uint32
if !lookupTransactionDataType(*x, &id) {
io.UnknownEnumOption(fmt.Sprintf("%T", x), "inventory transaction data type")
}
io.Varuint32(&id)
}
}
func PlayerInventoryAction(io protocol.IO, x *protocol.UseItemTransactionData) {
io.Varint32(&x.LegacyRequestID)
if x.LegacyRequestID < -1 && (x.LegacyRequestID&1) == 0 {
protocol.Slice(io, &x.LegacySetItemSlots)
}
protocol.Slice(io, &x.Actions)
io.Varuint32(&x.ActionType)
if IsProtoGTE(io, ID712) {
io.Varuint32(&x.TriggerType)
}
io.BlockPos(&x.BlockPosition)
io.Varint32(&x.BlockFace)
io.Varint32(&x.HotBarSlot)
io.ItemInstance(&x.HeldItem)
io.Vec3(&x.Position)
io.Vec3(&x.ClickedPosition)
io.Varuint32(&x.BlockRuntimeID)
if IsProtoGTE(io, ID712) {
io.Varuint32(&x.ClientPrediction)
}
}

View File

@@ -538,8 +538,10 @@ type CraftRecipeStackRequestAction struct {
// Marshal ...
func (a *CraftRecipeStackRequestAction) Marshal(r protocol.IO) {
r.Varuint32(&a.RecipeNetworkID)
if IsProtoGTE(r, ID712) {
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.

View File

@@ -87,7 +87,9 @@ func (x *TexturePackInfo) Marshal(r protocol.IO) {
r.String(&x.SubPackName)
r.String(&x.ContentIdentity)
r.Bool(&x.HasScripts)
if IsProtoGTE(r, ID712) {
r.Bool(&x.AddonPack)
}
r.Bool(&x.RTXEnabled)
if IsProtoGTE(r, ID748) {
r.String(&x.DownloadURL)

View File

@@ -233,6 +233,137 @@ func (p *Protocol) downgradePackets(pks []packet.Packet, conn *minecraft.Conn) [
Port: pk.Port,
ReloadWorld: pk.ReloadWorld,
}
case *packet.AddActor:
links := make([]proto.EntityLink, len(pk.EntityLinks))
for i, l := range pk.EntityLinks {
links[i] = (&proto.EntityLink{}).FromLatest(l)
}
pks[pkIndex] = &legacypacket.AddActor{
EntityUniqueID: pk.EntityUniqueID,
EntityRuntimeID: pk.EntityRuntimeID,
EntityType: pk.EntityType,
Position: pk.Position,
Velocity: pk.Velocity,
Pitch: pk.Pitch,
Yaw: pk.Yaw,
HeadYaw: pk.HeadYaw,
BodyYaw: pk.BodyYaw,
Attributes: pk.Attributes,
EntityMetadata: pk.EntityMetadata,
EntityProperties: pk.EntityProperties,
EntityLinks: links,
}
case *packet.AddPlayer:
links := make([]proto.EntityLink, len(pk.EntityLinks))
for i, l := range pk.EntityLinks {
links[i] = (&proto.EntityLink{}).FromLatest(l)
}
pks[pkIndex] = &legacypacket.AddPlayer{
UUID: pk.UUID,
Username: pk.Username,
EntityRuntimeID: pk.EntityRuntimeID,
PlatformChatID: pk.PlatformChatID,
Position: pk.Position,
Velocity: pk.Velocity,
Pitch: pk.Pitch,
Yaw: pk.Yaw,
HeadYaw: pk.HeadYaw,
HeldItem: pk.HeldItem,
GameType: pk.GameType,
EntityMetadata: pk.EntityMetadata,
EntityProperties: pk.EntityProperties,
AbilityData: pk.AbilityData,
EntityLinks: links,
DeviceID: pk.DeviceID,
BuildPlatform: pk.BuildPlatform,
}
case *packet.SetActorLink:
pks[pkIndex] = &legacypacket.SetActorLink{
EntityLink: (&proto.EntityLink{}).FromLatest(pk.EntityLink),
}
case *packet.CameraInstruction:
pks[pkIndex] = &legacypacket.CameraInstruction{
Set: pk.Set,
Clear: pk.Clear,
Fade: pk.Fade,
Target: pk.Target,
RemoveTarget: pk.RemoveTarget,
}
case *packet.ChangeDimension:
pks[pkIndex] = &legacypacket.ChangeDimension{
Dimension: pk.Dimension,
Position: pk.Position,
Respawn: pk.Respawn,
LoadingScreenID: pk.LoadingScreenID,
}
case *packet.CorrectPlayerMovePrediction:
pks[pkIndex] = &legacypacket.CorrectPlayerMovePrediction{
PredictionType: pk.PredictionType,
Position: pk.Position,
Delta: pk.Delta,
Rotation: pk.Rotation,
VehicleAngularVelocity: pk.VehicleAngularVelocity,
OnGround: pk.OnGround,
Tick: pk.Tick,
}
case *packet.Disconnect:
pks[pkIndex] = &legacypacket.Disconnect{
Reason: pk.Reason,
HideDisconnectionScreen: pk.HideDisconnectionScreen,
Message: pk.Message,
FilteredMessage: pk.FilteredMessage,
}
case *packet.EditorNetwork:
pks[pkIndex] = &legacypacket.EditorNetwork{
RouteToManager: pk.RouteToManager,
Payload: pk.Payload,
}
case *packet.MobArmourEquipment:
pks[pkIndex] = &legacypacket.MobArmourEquipment{
EntityRuntimeID: pk.EntityRuntimeID,
Helmet: pk.Helmet,
Chestplate: pk.Chestplate,
Leggings: pk.Leggings,
Boots: pk.Boots,
Body: pk.Body,
}
case *packet.PlayerArmourDamage:
pks[pkIndex] = &legacypacket.PlayerArmourDamage{
Bitset: pk.Bitset,
HelmetDamage: pk.HelmetDamage,
ChestplateDamage: pk.ChestplateDamage,
LeggingsDamage: pk.LeggingsDamage,
BootsDamage: pk.BootsDamage,
BodyDamage: pk.BodyDamage,
}
case *packet.SetTitle:
pks[pkIndex] = &legacypacket.SetTitle{
ActionType: pk.ActionType,
Text: pk.Text,
FadeInDuration: pk.FadeInDuration,
RemainDuration: pk.RemainDuration,
FadeOutDuration: pk.FadeOutDuration,
XUID: pk.XUID,
PlatformOnlineID: pk.PlatformOnlineID,
FilteredMessage: pk.FilteredMessage,
}
case *packet.StopSound:
pks[pkIndex] = &legacypacket.StopSound{
SoundName: pk.SoundName,
StopAll: pk.StopAll,
StopMusicLegacy: pk.StopMusicLegacy,
}
case *packet.InventoryTransaction:
trData := pk.TransactionData
if x, ok := trData.(*protocol.UseItemTransactionData); ok {
trData = (&proto.UseItemTransactionData{}).FromLatest(x)
}
pks[pkIndex] = &legacypacket.InventoryTransaction{
LegacyRequestID: pk.LegacyRequestID,
LegacySetItemSlots: pk.LegacySetItemSlots,
Actions: pk.Actions,
TransactionData: trData,
}
}
}
@@ -372,6 +503,137 @@ func (p *Protocol) upgradePackets(pks []packet.Packet, conn *minecraft.Conn) []p
Port: pk.Port,
ReloadWorld: pk.ReloadWorld,
}
case *legacypacket.AddActor:
links := make([]protocol.EntityLink, len(pk.EntityLinks))
for i, l := range pk.EntityLinks {
links[i] = l.ToLatest()
}
pks[pkIndex] = &packet.AddActor{
EntityUniqueID: pk.EntityUniqueID,
EntityRuntimeID: pk.EntityRuntimeID,
EntityType: pk.EntityType,
Position: pk.Position,
Velocity: pk.Velocity,
Pitch: pk.Pitch,
Yaw: pk.Yaw,
HeadYaw: pk.HeadYaw,
BodyYaw: pk.BodyYaw,
Attributes: pk.Attributes,
EntityMetadata: pk.EntityMetadata,
EntityProperties: pk.EntityProperties,
EntityLinks: links,
}
case *legacypacket.AddPlayer:
links := make([]protocol.EntityLink, len(pk.EntityLinks))
for i, l := range pk.EntityLinks {
links[i] = l.ToLatest()
}
pks[pkIndex] = &packet.AddPlayer{
UUID: pk.UUID,
Username: pk.Username,
EntityRuntimeID: pk.EntityRuntimeID,
PlatformChatID: pk.PlatformChatID,
Position: pk.Position,
Velocity: pk.Velocity,
Pitch: pk.Pitch,
Yaw: pk.Yaw,
HeadYaw: pk.HeadYaw,
HeldItem: pk.HeldItem,
GameType: pk.GameType,
EntityMetadata: pk.EntityMetadata,
EntityProperties: pk.EntityProperties,
AbilityData: pk.AbilityData,
EntityLinks: links,
DeviceID: pk.DeviceID,
BuildPlatform: pk.BuildPlatform,
}
case *legacypacket.SetActorLink:
pks[pkIndex] = &packet.SetActorLink{
EntityLink: pk.EntityLink.ToLatest(),
}
case *legacypacket.CameraInstruction:
pks[pkIndex] = &packet.CameraInstruction{
Set: pk.Set,
Clear: pk.Clear,
Fade: pk.Fade,
Target: pk.Target,
RemoveTarget: pk.RemoveTarget,
}
case *legacypacket.ChangeDimension:
pks[pkIndex] = &packet.ChangeDimension{
Dimension: pk.Dimension,
Position: pk.Position,
Respawn: pk.Respawn,
LoadingScreenID: pk.LoadingScreenID,
}
case *legacypacket.CorrectPlayerMovePrediction:
pks[pkIndex] = &packet.CorrectPlayerMovePrediction{
PredictionType: pk.PredictionType,
Position: pk.Position,
Delta: pk.Delta,
Rotation: pk.Rotation,
VehicleAngularVelocity: pk.VehicleAngularVelocity,
OnGround: pk.OnGround,
Tick: pk.Tick,
}
case *legacypacket.Disconnect:
pks[pkIndex] = &packet.Disconnect{
Reason: pk.Reason,
HideDisconnectionScreen: pk.HideDisconnectionScreen,
Message: pk.Message,
FilteredMessage: pk.FilteredMessage,
}
case *legacypacket.EditorNetwork:
pks[pkIndex] = &packet.EditorNetwork{
RouteToManager: pk.RouteToManager,
Payload: pk.Payload,
}
case *legacypacket.MobArmourEquipment:
pks[pkIndex] = &packet.MobArmourEquipment{
EntityRuntimeID: pk.EntityRuntimeID,
Helmet: pk.Helmet,
Chestplate: pk.Chestplate,
Leggings: pk.Leggings,
Boots: pk.Boots,
Body: pk.Body,
}
case *legacypacket.PlayerArmourDamage:
pks[pkIndex] = &packet.PlayerArmourDamage{
Bitset: pk.Bitset,
HelmetDamage: pk.HelmetDamage,
ChestplateDamage: pk.ChestplateDamage,
LeggingsDamage: pk.LeggingsDamage,
BootsDamage: pk.BootsDamage,
BodyDamage: pk.BodyDamage,
}
case *legacypacket.SetTitle:
pks[pkIndex] = &packet.SetTitle{
ActionType: pk.ActionType,
Text: pk.Text,
FadeInDuration: pk.FadeInDuration,
RemainDuration: pk.RemainDuration,
FadeOutDuration: pk.FadeOutDuration,
XUID: pk.XUID,
PlatformOnlineID: pk.PlatformOnlineID,
FilteredMessage: pk.FilteredMessage,
}
case *legacypacket.StopSound:
pks[pkIndex] = &packet.StopSound{
SoundName: pk.SoundName,
StopAll: pk.StopAll,
StopMusicLegacy: pk.StopMusicLegacy,
}
case *legacypacket.InventoryTransaction:
trData := pk.TransactionData
if x, ok := trData.(*proto.UseItemTransactionData); ok {
trData = x.ToLatest()
}
pks[pkIndex] = &packet.InventoryTransaction{
LegacyRequestID: pk.LegacyRequestID,
LegacySetItemSlots: pk.LegacySetItemSlots,
Actions: pk.Actions,
TransactionData: trData,
}
}
}
return pks

28
legacyver/v685.go Normal file
View File

@@ -0,0 +1,28 @@
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 (
// ItemVersion685 ...
ItemVersion685 = 191
// BlockVersion685 ...
BlockVersion685 int32 = (1 << 24) | (21 << 16) | (0 << 8)
)
// New685 uses same data as 686
func New685(direct bool) *Protocol {
itemMapping := mapping.NewItemMapping(itemRuntimeIDData686, requiredItemList686, ItemVersion685, false)
blockMapping := mapping.NewBlockMapping(blockStateData686)
return &Protocol{
ver: "1.21.0",
id: proto.ID685,
blockTranslator: NewBlockTranslator(blockMapping, latestBlockMapping, chunk.NewNetworkPersistentEncoding(blockMapping, BlockVersion685), chunk.NewBlockPaletteEncoding(blockMapping, BlockVersion685), false),
itemTranslator: NewItemTranslator(itemMapping, itemMappingLatest, blockMapping, blockMappingLatest),
}
}

36
legacyver/v686.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 (
// ItemVersion686 ...
ItemVersion686 = 191
// BlockVersion686 ...
BlockVersion686 int32 = (1 << 24) | (21 << 16) | (2 << 8)
)
var (
//go:embed data/item_runtime_ids_686.nbt
itemRuntimeIDData686 []byte
//go:embed data/required_item_list_686.json
requiredItemList686 []byte
//go:embed data/block_states_686.nbt
blockStateData686 []byte
)
func New686(direct bool) *Protocol {
itemMapping := mapping.NewItemMapping(itemRuntimeIDData686, requiredItemList686, ItemVersion686, false)
blockMapping := mapping.NewBlockMapping(blockStateData686)
return &Protocol{
ver: "1.21.2",
id: proto.ID686,
blockTranslator: NewBlockTranslator(blockMapping, latestBlockMapping, chunk.NewNetworkPersistentEncoding(blockMapping, BlockVersion686), chunk.NewBlockPaletteEncoding(blockMapping, BlockVersion686), false),
itemTranslator: NewItemTranslator(itemMapping, itemMappingLatest, blockMapping, blockMappingLatest),
}
}

View File

@@ -29,6 +29,10 @@ func main() {
StatusProvider: p,
AcceptedProtocols: []minecraft.Protocol{
legacyver.New748(false),
legacyver.New729(false),
legacyver.New712(false),
legacyver.New686(false),
legacyver.New685(false),
},
}.Listen("raknet", config.Connection.LocalAddress)
if err != nil {