update 1.21.130

This commit is contained in:
ethaniccc
2025-12-16 20:13:09 -05:00
29 changed files with 10611 additions and 64 deletions

View File

@@ -11,15 +11,16 @@ import (
type Animate struct {
// ActionType is the ID of the animation action to execute. It is one of the action type constants that
// may be found above.
ActionType int32
ActionType uint8
// EntityRuntimeID is the runtime ID of the player that the animation should be played upon. The runtime
// ID is unique for each world session, and entities are generally identified in packets using this
// runtime ID.
EntityRuntimeID uint64
// Data ...
Data float32
// RowingTime is the time for rowing actions.
RowingTime float32
// SwingSource is the source for swing actions. It is one of the action type constants that
// may be found above.
SwingSource protocol.Optional[string]
}
// ID ...
@@ -28,16 +29,18 @@ func (*Animate) ID() uint32 {
}
func (pk *Animate) Marshal(io protocol.IO) {
io.Varint32(&pk.ActionType)
io.Varuint64(&pk.EntityRuntimeID)
if proto.IsProtoGTE(io, proto.ID859) {
io.Float32(&pk.Data)
if pk.ActionType == packet.AnimateActionRowLeft || pk.ActionType == packet.AnimateActionRowRight {
io.Float32(&pk.RowingTime)
}
if proto.IsProtoGTE(io, proto.ID898) {
io.Uint8(&pk.ActionType)
} else {
if pk.ActionType&0x80 != 0 {
io.Float32(&pk.RowingTime)
}
v := int32(pk.ActionType)
io.Varint32(&v)
pk.ActionType = uint8(v)
}
io.Varuint64(&pk.EntityRuntimeID)
if proto.IsProtoGTE(io, proto.ID859) || (pk.ActionType == 128 || pk.ActionType == 127) {
io.Float32(&pk.Data)
}
if proto.IsProtoGTE(io, proto.ID898) {
protocol.OptionalFunc(io, &pk.SwingSource, io.String)
}
}

View File

@@ -0,0 +1,59 @@
package legacypacket
import (
"github.com/akmalfairuz/legacy-version/legacyver/proto"
"github.com/sandertv/gophertunnel/minecraft/protocol"
"github.com/sandertv/gophertunnel/minecraft/protocol/packet"
)
// AvailableCommands is sent by the server to send a list of all commands that
// the player is able to use on the server. This packet holds all the arguments
// of each commands as well, making it possible for the client to provide
// auto-completion and command usages. AvailableCommands packets can be resent,
// but the packet is often very big, so doing this very often should be avoided.
type AvailableCommands struct {
// EnumValues is a slice of all enum values of any enum in the
// AvailableCommands packet. EnumValues generally should contain each
// possible value only once. Enums are built by pointing to entries in this
// slice.
EnumValues []string
// ChainedSubcommandValues is a slice of all chained subcommand names. ChainedSubcommandValues generally should
//contain each possible value only once. ChainedSubcommands are built by pointing to entries in this slice.
ChainedSubcommandValues []string
// Suffixes, like EnumValues, is a slice of all suffix values of any command
// parameter in the AvailableCommands packet.
Suffixes []string
// Enums is a slice of all (fixed) command enums present in any of the
// commands.
Enums []proto.CommandEnum
// ChainedSubcommands is a slice of all subcommands that are followed by a chained command. An example usage of this
// is /execute which allows you to run another command as another entity or at a different position etc.
ChainedSubcommands []proto.ChainedSubcommand
// Commands is a list of all commands that the client should show
// client-side. The AvailableCommands packet replaces any commands sent
// before. It does not only add the commands that are sent in it.
Commands []proto.Command
// DynamicEnums is a slice of dynamic command enums. These command enums can
// be changed during runtime without having to resend an AvailableCommands
// packet.
DynamicEnums []protocol.DynamicEnum
// Constraints is a list of constraints that should be applied to certain
// options of enums in the commands above.
Constraints []protocol.CommandEnumConstraint
}
// ID ...
func (*AvailableCommands) ID() uint32 {
return packet.IDAvailableCommands
}
func (pk *AvailableCommands) Marshal(io protocol.IO) {
protocol.FuncSlice(io, &pk.EnumValues, io.String)
protocol.FuncSlice(io, &pk.ChainedSubcommandValues, io.String)
protocol.FuncSlice(io, &pk.Suffixes, io.String)
protocol.FuncIOSlice(io, &pk.Enums, proto.CommandEnumContext{EnumValues: pk.EnumValues}.Marshal)
protocol.Slice(io, &pk.ChainedSubcommands)
protocol.Slice(io, &pk.Commands)
protocol.Slice(io, &pk.DynamicEnums)
protocol.Slice(io, &pk.Constraints)
}

View File

@@ -6,7 +6,10 @@ import (
"github.com/sandertv/gophertunnel/minecraft/protocol/packet"
)
const ClientMovementPredictionSyncBitsetSize = 123
const (
ClientMovementPredictionSyncBitsetSize786 = 123
ClientMovementPredictionSyncBitsetSize898 = 124
)
// ClientMovementPredictionSync is sent by the client to the server periodically if the client has received
// movement corrections from the server, containing information about client-predictions that are relevant
@@ -45,11 +48,7 @@ func (*ClientMovementPredictionSync) ID() uint32 {
}
func (pk *ClientMovementPredictionSync) Marshal(io protocol.IO) {
if proto.IsProtoGTE(io, proto.ID786) {
io.Bitset(&pk.ActorFlags, ClientMovementPredictionSyncBitsetSize)
} else {
io.Bitset(&pk.ActorFlags, 120)
}
io.Bitset(&pk.ActorFlags, proto.EntityDataFlagsLength(proto.FetchProtoID(io)))
io.Float32(&pk.BoundingBoxScale)
io.Float32(&pk.BoundingBoxWidth)
io.Float32(&pk.BoundingBoxHeight)

View File

@@ -0,0 +1,86 @@
package legacypacket
import (
"github.com/akmalfairuz/legacy-version/legacyver/proto"
"github.com/sandertv/gophertunnel/minecraft/protocol"
"github.com/sandertv/gophertunnel/minecraft/protocol/packet"
)
// CommandOutput is sent by the server to the client to send text as output of a command. Most servers do not
// use this packet and instead simply send Text packets, but there is reason to send it.
// If the origin of a CommandRequest packet is not the player itself, but, for example, a websocket server,
// sending a Text packet will not do what is expected: The message should go to the websocket server, not to
// the client's chat. The CommandOutput packet will make sure the messages are relayed to the correct origin
// of the command request.
type CommandOutput struct {
// CommandOrigin is the data specifying the origin of the command. In other words, the source that the
// command request was from, such as the player itself or a websocket server. The client forwards the
// messages in this packet to the right origin, depending on what is sent here.
CommandOrigin protocol.CommandOrigin
// OutputType specifies the type of output that is sent. The OutputType sent by vanilla games appears to
// be 3, which seems to work.
OutputType string
// SuccessCount is the amount of times that a command was executed successfully as a result of the command
// that was requested. For servers, this is usually a rather meaningless fields, but for vanilla, this is
// applicable for commands created with Functions.
SuccessCount uint32
// OutputMessages is a list of all output messages that should be sent to the player. Whether they are
// shown or not, depends on the type of the messages.
OutputMessages []proto.CommandOutputMessage
// DataSet ... TODO: Find out what this is for.
DataSet protocol.Optional[string]
}
// ID ...
func (*CommandOutput) ID() uint32 {
return packet.IDCommandOutput
}
func (pk *CommandOutput) Marshal(io protocol.IO) {
proto.CommandOriginData(io, &pk.CommandOrigin)
if proto.IsProtoGTE(io, proto.ID898) {
io.String(&pk.OutputType)
io.Uint32(&pk.SuccessCount)
} else {
v := LegacyCommandOutputType(pk.OutputType)
io.Uint8(&v)
pk.OutputType = LatestCommandOutputType(v)
io.Varuint32(&pk.SuccessCount)
}
protocol.Slice(io, &pk.OutputMessages)
if proto.IsProtoGTE(io, proto.ID898) {
protocol.OptionalFunc(io, &pk.DataSet, io.String)
} else if pk.OutputType == "dataset" {
v, _ := pk.DataSet.Value()
io.String(&v)
if v != "" {
pk.DataSet = protocol.Option(v)
} else {
pk.DataSet = protocol.Optional[string]{}
}
}
}
var legacyCommandOutputTypeMap = map[byte]string{
0: "none", // packet.CommandOutputTypeNone
1: "lastoutput", // packet.CommandOutputTypeLastOutput
2: "silent", // packet.CommandOutputTypeSilent
3: "alloutput", // packet.CommandOutputTypeAllOutput
4: "dataset", // packet.CommandOutputTypeDataSet
}
func LegacyCommandOutputType(outputType string) byte {
for k, v := range legacyCommandOutputTypeMap {
if v == outputType {
return k
}
}
return 3 // Default to all output.
}
func LatestCommandOutputType(outputType byte) string {
if v, ok := legacyCommandOutputTypeMap[outputType]; ok {
return v
}
return "alloutput" // packet.CommandOutputTypeAllOutput
}

View File

@@ -0,0 +1,43 @@
package legacypacket
import (
"github.com/akmalfairuz/legacy-version/internal/typeconf"
"github.com/akmalfairuz/legacy-version/legacyver/proto"
"github.com/sandertv/gophertunnel/minecraft/protocol"
"github.com/sandertv/gophertunnel/minecraft/protocol/packet"
)
// CommandRequest is sent by the client to request the execution of a server-side command. Although some
// servers support sending commands using the Text packet, this packet is guaranteed to have the correct
// result.
type CommandRequest struct {
// CommandLine is the raw entered command line. The client does no parsing of the command line by itself
// (unlike it did in the early stages), but lets the server do that.
CommandLine string
// CommandOrigin is the data specifying the origin of the command. In other words, the source that the
// command was from, such as the player itself or a websocket server.
CommandOrigin protocol.CommandOrigin
// Internal specifies if the command request internal. Setting it to false seems to work and the usage of
// this field is not known.
Internal bool
// Version is the version of the command that is being executed. This field currently has no purpose or functionality.
Version string
}
// ID ...
func (*CommandRequest) ID() uint32 {
return packet.IDCommandRequest
}
func (pk *CommandRequest) Marshal(io protocol.IO) {
io.String(&pk.CommandLine)
proto.CommandOriginData(io, &pk.CommandOrigin)
io.Bool(&pk.Internal)
if proto.IsProtoGTE(io, proto.ID898) {
io.String(&pk.Version)
} else {
v := typeconf.StringToInt[int32](pk.Version, 0)
io.Varint32(&v)
pk.Version = typeconf.IntToString[int32](v, "")
}
}

View File

@@ -0,0 +1,39 @@
package legacypacket
import (
"github.com/akmalfairuz/legacy-version/internal/typeconf"
"github.com/akmalfairuz/legacy-version/legacyver/proto"
"github.com/sandertv/gophertunnel/minecraft/protocol"
"github.com/sandertv/gophertunnel/minecraft/protocol/packet"
)
// Event is sent by the server to send an event with additional data. It is typically sent to the client for
// telemetry reasons, much like the SimpleEvent packet.
type Event struct {
// 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 int64
// UsePlayerID ...
// TODO: Figure out what UsePlayerID is for.
UsePlayerID bool
// Event is the event that is transmitted.
Event protocol.Event
}
// ID ...
func (*Event) ID() uint32 {
return packet.IDEvent
}
func (pk *Event) Marshal(io protocol.IO) {
io.Varint64(&pk.EntityRuntimeID)
io.EventType(&pk.Event)
if proto.IsProtoGTE(io, proto.ID898) {
io.Bool(&pk.UsePlayerID)
} else {
v := typeconf.BoolToByte(pk.UsePlayerID)
io.Uint8(&v)
pk.UsePlayerID = typeconf.ByteToBool(v)
}
pk.Event.Marshal(io)
}

View File

@@ -0,0 +1,44 @@
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"
)
// Interact is sent by the client when it interacts with another entity in some way. It used to be used for
// normal entity and block interaction, but this is no longer the case now.
type Interact struct {
// Action type is the ID of the action that was executed by the player. It is one of the constants that
// may be found above.
ActionType byte
// TargetEntityRuntimeID is the runtime ID of the entity that the player interacted with. This is empty
// for the InteractActionOpenInventory action type.
TargetEntityRuntimeID uint64
// Position associated with the ActionType above. For the InteractActionMouseOverEntity, this is the
// position relative to the entity moused over over which the player hovered with its mouse/touch. For the
// InteractActionLeaveVehicle, this is the position that the player spawns at after leaving the vehicle.
Position protocol.Optional[mgl32.Vec3]
}
// ID ...
func (*Interact) ID() uint32 {
return packet.IDInteract
}
func (pk *Interact) Marshal(io protocol.IO) {
io.Uint8(&pk.ActionType)
io.Varuint64(&pk.TargetEntityRuntimeID)
if proto.IsProtoGTE(io, proto.ID898) {
protocol.OptionalFunc(io, &pk.Position, io.Vec3)
} else if pk.ActionType == packet.InteractActionMouseOverEntity || pk.ActionType == packet.InteractActionLeaveVehicle {
pos, _ := pk.Position.Value()
io.Vec3(&pos)
if pos != (mgl32.Vec3{}) {
pk.Position = protocol.Option(pos)
} else {
pk.Position = protocol.Optional[mgl32.Vec3]{}
}
}
}

View File

@@ -30,6 +30,8 @@ type MobEffect struct {
Duration int32
// Tick is the server tick at which the packet was sent. It is used in relation to CorrectPlayerMovePrediction.
Tick uint64
// Ambient specifies if the effect is ambient. If set to false, it will not get treated as an ambient effect.
Ambient bool
}
// ID ...
@@ -50,5 +52,9 @@ func (pk *MobEffect) Marshal(io protocol.IO) {
} else {
io.Uint64(&pk.Tick)
}
if proto.IsProtoGTE(io, proto.ID898) {
io.Bool(&pk.Ambient)
}
}
}

View File

@@ -13,9 +13,6 @@ type ResourcePackStack struct {
// 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
// BehaviourPack is a list of behaviour packs that the client needs to download before joining the server.
// All of these behaviour packs will be applied together, and the order does not necessarily matter.
BehaviourPacks []protocol.StackResourcePack
// TexturePacks is a list of texture packs that the client needs to download before joining the server.
// The order of these texture packs specifies the order that they are applied in on the client side. The
// first in the list will be applied first.
@@ -41,7 +38,10 @@ func (*ResourcePackStack) ID() uint32 {
func (pk *ResourcePackStack) Marshal(io protocol.IO) {
io.Bool(&pk.TexturePackRequired)
protocol.Slice(io, &pk.BehaviourPacks)
if proto.IsProtoLT(io, proto.ID898) {
var emptyResourcePack []protocol.StackResourcePack
protocol.Slice(io, &emptyResourcePack)
}
protocol.Slice(io, &pk.TexturePacks)
io.String(&pk.BaseGameVersion)
protocol.SliceUint32Length(io, &pk.Experiments)

View File

@@ -238,8 +238,6 @@ type StartGame struct {
// its index in the expected block palette. This is useful for servers that wish to support multiple protocol versions
// and custom blocks, but it will result in extra bytes being written for every block in a sub chunk palette.
UseBlockNetworkIDHashes bool
// TickDeathSystemsEnabled specifies if the new tick death systems are enabled.
TickDeathSystemsEnabled bool
// ServerAuthoritativeSound is currently unknown as to what it does.
ServerAuthoritativeSound bool
}
@@ -338,8 +336,9 @@ func (pk *StartGame) Marshal(io protocol.IO) {
io.UUID(&pk.WorldTemplateID)
io.Bool(&pk.ClientSideGeneration)
io.Bool(&pk.UseBlockNetworkIDHashes)
if proto.IsProtoGTE(io, proto.ID827) {
io.Bool(&pk.TickDeathSystemsEnabled)
if proto.IsProtoGTE(io, proto.ID827) && proto.IsProtoLT(io, proto.ID898) {
v := false
io.Bool(&v)
}
io.Bool(&pk.ServerAuthoritativeSound)
}

View File

@@ -50,7 +50,7 @@ type Text struct {
PlatformChatID 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
FilteredMessage protocol.Optional[string]
}
// ID ...
@@ -59,8 +59,22 @@ func (*Text) ID() uint32 {
}
func (pk *Text) Marshal(io protocol.IO) {
io.Uint8(&pk.TextType)
if proto.IsProtoLT(io, proto.ID898) {
io.Uint8(&pk.TextType)
}
io.Bool(&pk.NeedsTranslation)
if proto.IsProtoGTE(io, proto.ID898) {
var categoryType uint8
if pk.TextType == TextTypeRaw || pk.TextType == TextTypeTip || pk.TextType == TextTypeSystem || pk.TextType == TextTypeObjectWhisper || pk.TextType == TextTypeObjectAnnouncement || pk.TextType == TextTypeObject {
categoryType = protocol.TextCategoryMessageOnly
} else if pk.TextType == TextTypeChat || pk.TextType == TextTypeWhisper || pk.TextType == TextTypeAnnouncement {
categoryType = protocol.TextCategoryAuthoredMessage
} else {
categoryType = protocol.TextCategoryMessageWithParameters
}
io.TextCategory(&categoryType)
io.Uint8(&pk.TextType)
}
switch pk.TextType {
case TextTypeChat, TextTypeWhisper, TextTypeAnnouncement:
io.String(&pk.SourceName)
@@ -71,9 +85,24 @@ func (pk *Text) Marshal(io protocol.IO) {
io.String(&pk.Message)
protocol.FuncSlice(io, &pk.Parameters, io.String)
}
if proto.IsProtoGTE(io, proto.ID898) {
if len(pk.Message) == 0 {
io.InvalidValue(pk.Message, "message", "string cannot be empty")
}
}
io.String(&pk.XUID)
io.String(&pk.PlatformChatID)
if proto.IsProtoGTE(io, proto.ID685) {
io.String(&pk.FilteredMessage)
if proto.IsProtoGTE(io, proto.ID898) {
protocol.OptionalFunc(io, &pk.FilteredMessage, io.String)
} else {
v, _ := pk.FilteredMessage.Value()
io.String(&v)
if v != "" {
pk.FilteredMessage = protocol.Option(v)
} else {
pk.FilteredMessage = protocol.Optional[string]{}
}
}
}
}