diff --git a/go.mod b/go.mod index a47f785d..d7f7a3c2 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,7 @@ require ( github.com/go-gl/mathgl v1.0.0 github.com/google/uuid v1.3.0 github.com/klauspost/compress v1.15.1 + github.com/kr/pretty v0.1.0 github.com/muhammadmuzzammil1998/jsonc v1.0.0 github.com/pelletier/go-toml v1.9.4 github.com/sandertv/go-raknet v1.10.9 @@ -19,6 +20,7 @@ require ( require ( github.com/df-mc/atomic v1.10.0 // indirect github.com/golang/protobuf v1.4.2 // indirect + github.com/kr/text v0.1.0 // indirect golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 // indirect golang.org/x/image v0.0.0-20190802002840-cff245a6509b // indirect google.golang.org/appengine v1.6.6 // indirect diff --git a/go.sum b/go.sum index 1e03fff4..bae1b827 100644 --- a/go.sum +++ b/go.sum @@ -110,8 +110,10 @@ github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/X github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.15.1 h1:y9FcTHGyrebwfP0ZZqFiaxTaiDnUrGkJkI+f583BL1A= github.com/klauspost/compress v1.15.1/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/muhammadmuzzammil1998/jsonc v1.0.0 h1:8o5gBQn4ZA3NBA9DlTujCj2a4w0tqWrPVjDwhzkgTIs= github.com/muhammadmuzzammil1998/jsonc v1.0.0/go.mod h1:saF2fIVw4banK0H4+/EuqfFLpRnoy5S+ECwTOCcRcSU= diff --git a/minecraft/conn.go b/minecraft/conn.go index ac82fd25..e1b14a6a 100644 --- a/minecraft/conn.go +++ b/minecraft/conn.go @@ -944,6 +944,7 @@ func (conn *Conn) startGame() { Yaw: data.Yaw, Dimension: data.Dimension, WorldSpawn: data.WorldSpawn, + EditorWorld: data.EditorWorld, GameRules: data.GameRules, Time: data.Time, Blocks: data.CustomBlocks, @@ -1132,6 +1133,7 @@ func (conn *Conn) handleStartGame(pk *packet.StartGame) error { Yaw: pk.Yaw, Dimension: pk.Dimension, WorldSpawn: pk.WorldSpawn, + EditorWorld: pk.EditorWorld, GameRules: pk.GameRules, Time: pk.Time, ServerBlockStateChecksum: pk.ServerBlockStateChecksum, @@ -1243,6 +1245,12 @@ func (conn *Conn) handlePlayStatus(pk *packet.PlayStatus) error { case packet.PlayStatusLoginFailedServerFull: _ = conn.Close() return fmt.Errorf("server full") + case packet.PlayStatusLoginFailedEditorVanilla: + _ = conn.Close() + return fmt.Errorf("cannot join a vanilla game on editor") + case packet.PlayStatusLoginFailedVanillaEditor: + _ = conn.Close() + return fmt.Errorf("cannot join an editor game on vanilla") default: return fmt.Errorf("unknown play status in PlayStatus packet %v", pk.Status) } diff --git a/minecraft/game_data.go b/minecraft/game_data.go index 629b5041..5b2d151a 100644 --- a/minecraft/game_data.go +++ b/minecraft/game_data.go @@ -46,6 +46,9 @@ type GameData struct { // WorldSpawn is the block on which the world spawn of the world. This coordinate has no effect on the // place that the client spawns, but it does have an effect on the direction that a compass points. WorldSpawn protocol.BlockPos + // EditorWorld is a value to dictate if the world is in editor mode, a special mode recently introduced adding + // "powerful tools for editing worlds, intended for experienced creators." + EditorWorld bool // WorldGameMode is the game mode that a player gets when it first spawns in the world. It is shown in the // settings and is used if the PlayerGameMode is set to 5. WorldGameMode int32 diff --git a/minecraft/protocol/ability.go b/minecraft/protocol/ability.go new file mode 100644 index 00000000..6c97fbac --- /dev/null +++ b/minecraft/protocol/ability.go @@ -0,0 +1,60 @@ +package protocol + +const ( + AbilityBuild = 1 << iota + AbilityMine + AbilityDoorsAndSwitches + AbilityOpenContainers + AbilityAttackPlayers + AbilityAttackMobs + AbilityOperatorCommands + AbilityTeleport + AbilityInvulnerable + AbilityFlying + AbilityMayFly + AbilityInstantBuild + AbilityLightning + AbilityFlySpeed + AbilityWalkSpeed + AbilityMuted + AbilityWorldBuilder + AbilityNoClip + AbilityCount +) + +const ( + AbilityLayerTypeCustomCache = iota + AbilityLayerTypeBase + AbilityLayerTypeSpectator + AbilityLayerTypeCommands +) + +const ( + AbilityBaseFlySpeed = 0.05 + AbilityBaseWalkSpeed = 0.1 +) + +// AbilityLayer represents the abilities of a specific layer, such as the base layer or the spectator layer. +type AbilityLayer struct { + // Type represents the type of the layer. This is one of the AbilityLayerType constants defined above. + Type uint16 + // Abilities is a set of abilities that are enabled for the layer. This is one of the Ability constants defined + // above. + Abilities uint32 + // Values is a set of values that are associated with the enabled abilities, representing the values of the + // abilities. + Values uint32 + // FlySpeed is the default fly speed of the layer. + FlySpeed float32 + // WalkSpeed is the default walk speed of the layer. + WalkSpeed float32 +} + +// SerializedLayer reads/writes a AbilityLayer x using IO r. +func SerializedLayer(r IO, x *AbilityLayer) { + r.Uint16(&x.Type) + r.Uint32(&x.Abilities) + r.Uint32(&x.Values) + r.Float32(&x.FlySpeed) + r.Float32(&x.WalkSpeed) +} diff --git a/minecraft/protocol/info.go b/minecraft/protocol/info.go index 2adeb6a5..18c3d2ef 100644 --- a/minecraft/protocol/info.go +++ b/minecraft/protocol/info.go @@ -2,7 +2,7 @@ package protocol const ( // CurrentProtocol is the current protocol version for the version below. - CurrentProtocol = 527 + CurrentProtocol = 534 // CurrentVersion is the current version of Minecraft as supported by the `packet` package. - CurrentVersion = "1.19.0" + CurrentVersion = "1.19.10" ) diff --git a/minecraft/protocol/packet/actor_event.go b/minecraft/protocol/packet/actor_event.go index e0546bc9..aefde87d 100644 --- a/minecraft/protocol/packet/actor_event.go +++ b/minecraft/protocol/packet/actor_event.go @@ -68,6 +68,7 @@ const ( ActorEventLandedOnGround ActorEventActorGrowUp ActorEventVibrationDetected + ActorEventDrinkMilk ) // ActorEvent is sent by the server when a particular event happens that has to do with an entity. Some of diff --git a/minecraft/protocol/packet/add_actor.go b/minecraft/protocol/packet/add_actor.go index 6579f309..df7ece2e 100644 --- a/minecraft/protocol/packet/add_actor.go +++ b/minecraft/protocol/packet/add_actor.go @@ -29,9 +29,13 @@ type AddActor struct { 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 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.Attribute @@ -60,6 +64,7 @@ func (pk *AddActor) Marshal(w *protocol.Writer) { w.Float32(&pk.Pitch) w.Float32(&pk.Yaw) w.Float32(&pk.HeadYaw) + w.Float32(&pk.BodyYaw) protocol.WriteInitialAttributes(w, &pk.Attributes) w.EntityMetadata(&pk.EntityMetadata) protocol.WriteEntityLinks(w, &pk.EntityLinks) @@ -75,6 +80,7 @@ func (pk *AddActor) Unmarshal(r *protocol.Reader) { r.Float32(&pk.Pitch) r.Float32(&pk.Yaw) r.Float32(&pk.HeadYaw) + r.Float32(&pk.BodyYaw) protocol.InitialAttributes(r, &pk.Attributes) r.EntityMetadata(&pk.EntityMetadata) protocol.EntityLinks(r, &pk.EntityLinks) diff --git a/minecraft/protocol/packet/add_player.go b/minecraft/protocol/packet/add_player.go index 43d8160d..835a3c37 100644 --- a/minecraft/protocol/packet/add_player.go +++ b/minecraft/protocol/packet/add_player.go @@ -16,10 +16,6 @@ type AddPlayer struct { // 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 - // EntityUniqueID is the unique ID of the player. 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 player out for - // this field. - EntityUniqueID int64 // 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 @@ -51,22 +47,17 @@ type AddPlayer struct { // 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 - // Flags is a set of flags that specify certain properties of the player, such as whether it can - // fly and/or move through blocks. - Flags uint32 - // CommandPermissionLevel is a set of permissions that specify what commands a player is allowed to execute. - CommandPermissionLevel uint32 - // ActionPermissions is, much like Flags, a set of flags that specify actions that the player is allowed - // to undertake, such as whether it is allowed to edit blocks, open doors etc. - ActionPermissions uint32 - // PermissionLevel is the permission level of the player as it shows up in the player list built up using - // the PlayerList packet. - PermissionLevel uint32 - // CustomStoredPermissions ... - CustomStoredPermissions uint32 - // PlayerUniqueID is a unique identifier of the player. It appears it is not required to fill this field + // EntityUniqueID is a unique identifier of the player. It appears it is not required to fill this field // out with a correct value. Simply writing 0 seems to work. - PlayerUniqueID int64 + EntityUniqueID int64 + // PlayerPermissions is the permission level of the player as it shows up in the player list built up using + // the PlayerList packet. + PlayerPermissions byte + // CommandPermissions is a set of permissions that specify what commands a player is allowed to execute. + CommandPermissions byte + // Layers contains all ability layers and their potential values. This should at least have one entry, being the + // base layer. + Layers []protocol.AbilityLayer // 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. @@ -88,7 +79,6 @@ func (*AddPlayer) ID() uint32 { func (pk *AddPlayer) Marshal(w *protocol.Writer) { w.UUID(&pk.UUID) w.String(&pk.Username) - w.Varint64(&pk.EntityUniqueID) w.Varuint64(&pk.EntityRuntimeID) w.String(&pk.PlatformChatID) w.Vec3(&pk.Position) @@ -99,12 +89,14 @@ func (pk *AddPlayer) Marshal(w *protocol.Writer) { w.ItemInstance(&pk.HeldItem) w.Varint32(&pk.GameType) w.EntityMetadata(&pk.EntityMetadata) - w.Varuint32(&pk.Flags) - w.Varuint32(&pk.CommandPermissionLevel) - w.Varuint32(&pk.ActionPermissions) - w.Varuint32(&pk.PermissionLevel) - w.Varuint32(&pk.CustomStoredPermissions) - w.Int64(&pk.PlayerUniqueID) + w.Int64(&pk.EntityUniqueID) + w.Uint8(&pk.PlayerPermissions) + w.Uint8(&pk.CommandPermissions) + layersLen := uint8(len(pk.Layers)) + w.Uint8(&layersLen) + for _, layer := range pk.Layers { + protocol.SerializedLayer(w, &layer) + } protocol.WriteEntityLinks(w, &pk.EntityLinks) w.String(&pk.DeviceID) w.Int32(&pk.BuildPlatform) @@ -114,7 +106,6 @@ func (pk *AddPlayer) Marshal(w *protocol.Writer) { func (pk *AddPlayer) Unmarshal(r *protocol.Reader) { r.UUID(&pk.UUID) r.String(&pk.Username) - r.Varint64(&pk.EntityUniqueID) r.Varuint64(&pk.EntityRuntimeID) r.String(&pk.PlatformChatID) r.Vec3(&pk.Position) @@ -125,12 +116,15 @@ func (pk *AddPlayer) Unmarshal(r *protocol.Reader) { r.ItemInstance(&pk.HeldItem) r.Varint32(&pk.GameType) r.EntityMetadata(&pk.EntityMetadata) - r.Varuint32(&pk.Flags) - r.Varuint32(&pk.CommandPermissionLevel) - r.Varuint32(&pk.ActionPermissions) - r.Varuint32(&pk.PermissionLevel) - r.Varuint32(&pk.CustomStoredPermissions) - r.Int64(&pk.PlayerUniqueID) + r.Int64(&pk.EntityUniqueID) + r.Uint8(&pk.PlayerPermissions) + r.Uint8(&pk.CommandPermissions) + var layersLen uint8 + r.Uint8(&layersLen) + pk.Layers = make([]protocol.AbilityLayer, layersLen) + for i := uint8(0); i < layersLen; i++ { + protocol.SerializedLayer(r, &pk.Layers[i]) + } protocol.EntityLinks(r, &pk.EntityLinks) r.String(&pk.DeviceID) r.Int32(&pk.BuildPlatform) diff --git a/minecraft/protocol/packet/death_info.go b/minecraft/protocol/packet/death_info.go new file mode 100644 index 00000000..b21e321c --- /dev/null +++ b/minecraft/protocol/packet/death_info.go @@ -0,0 +1,40 @@ +package packet + +import ( + "github.com/sandertv/gophertunnel/minecraft/protocol" +) + +// DeathInfo is a packet sent from the server to the client expected to be sent when a player dies. It contains messages +// related to the player's death, which are shown on the death screen as of v1.19.10. +type DeathInfo struct { + // Cause is the cause of the player's death, such as "suffocation" or "suicide". + Cause string + // Messages is a list of death messages to be shown on the death screen. + Messages []string +} + +// ID ... +func (*DeathInfo) ID() uint32 { + return IDDeathInfo +} + +// Marshal ... +func (pk *DeathInfo) Marshal(w *protocol.Writer) { + w.String(&pk.Cause) + messageLen := uint32(len(pk.Messages)) + w.Varuint32(&messageLen) + for _, message := range pk.Messages { + w.String(&message) + } +} + +// Unmarshal ... +func (pk *DeathInfo) Unmarshal(r *protocol.Reader) { + r.String(&pk.Cause) + var messageLen uint32 + r.Varuint32(&messageLen) + pk.Messages = make([]string, messageLen) + for i := uint32(0); i < messageLen; i++ { + r.String(&pk.Messages[i]) + } +} diff --git a/minecraft/protocol/packet/editor_network.go b/minecraft/protocol/packet/editor_network.go new file mode 100644 index 00000000..9d8ed368 --- /dev/null +++ b/minecraft/protocol/packet/editor_network.go @@ -0,0 +1,28 @@ +package packet + +import ( + "github.com/sandertv/gophertunnel/minecraft/nbt" + "github.com/sandertv/gophertunnel/minecraft/protocol" +) + +// 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 { + // Payload is a network little endian compound tag holding data relevant to the editor. + Payload map[string]any +} + +// ID ... +func (*EditorNetwork) ID() uint32 { + return IDEditorNetwork +} + +// Marshal ... +func (pk *EditorNetwork) Marshal(w *protocol.Writer) { + w.NBT(&pk.Payload, nbt.NetworkLittleEndian) +} + +// Unmarshal ... +func (pk *EditorNetwork) Unmarshal(r *protocol.Reader) { + r.NBT(&pk.Payload, nbt.NetworkLittleEndian) +} diff --git a/minecraft/protocol/packet/id.go b/minecraft/protocol/packet/id.go index fc7473ba..98f0d0c1 100644 --- a/minecraft/protocol/packet/id.go +++ b/minecraft/protocol/packet/id.go @@ -187,4 +187,8 @@ const ( IDRequestAbility IDRequestPermissions IDToastRequest + IDUpdateAbilities + IDUpdateAdventureSettings + IDDeathInfo + IDEditorNetwork ) diff --git a/minecraft/protocol/packet/level_sound_event.go b/minecraft/protocol/packet/level_sound_event.go index 263fb088..16d175df 100644 --- a/minecraft/protocol/packet/level_sound_event.go +++ b/minecraft/protocol/packet/level_sound_event.go @@ -398,38 +398,38 @@ const ( SoundEventGoatCall5 SoundEventGoatCall6 SoundEventGoatCall7 - SoundEventGoatCall8 - SoundEventGoatCall9 - SoundEventGoatHarmony0 - SoundEventGoatHarmony1 - SoundEventGoatHarmony2 - SoundEventGoatHarmony3 - SoundEventGoatHarmony4 - SoundEventGoatHarmony5 - SoundEventGoatHarmony6 - SoundEventGoatHarmony7 - SoundEventGoatHarmony8 - SoundEventGoatHarmony9 - SoundEventGoatMelody0 - SoundEventGoatMelody1 - SoundEventGoatMelody2 - SoundEventGoatMelody3 - SoundEventGoatMelody4 - SoundEventGoatMelody5 - SoundEventGoatMelody6 - SoundEventGoatMelody7 - SoundEventGoatMelody8 - SoundEventGoatMelody9 - SoundEventGoatBass0 - SoundEventGoatBass1 - SoundEventGoatBass2 - SoundEventGoatBass3 - SoundEventGoatBass4 - SoundEventGoatBass5 - SoundEventGoatBass6 - SoundEventGoatBass7 - SoundEventGoatBass8 - SoundEventGoatBass9 + _ + _ + _ + _ + _ + _ + _ + _ + _ + _ + _ + _ + _ + _ + _ + _ + _ + _ + _ + _ + _ + _ + _ + _ + _ + _ + _ + _ + _ + _ + _ + _ _ _ _ @@ -439,7 +439,7 @@ const ( SoundEventItemTaken SoundEventDisappeared SoundEventReappeared - _ + SoundEventDrinkMilk SoundEventFrogspawnHatched SoundEventLaySpawn SoundEventFrogspawnBreak @@ -448,6 +448,7 @@ const ( SoundeventItemThrown SoundEventRecord5 SoundEventConvertToFrog + SoundEventRecordPlaying SoundEventUndefined ) diff --git a/minecraft/protocol/packet/play_status.go b/minecraft/protocol/packet/play_status.go index b4f3610c..2d28ec07 100644 --- a/minecraft/protocol/packet/play_status.go +++ b/minecraft/protocol/packet/play_status.go @@ -13,6 +13,8 @@ const ( PlayStatusLoginFailedVanillaEdu PlayStatusLoginFailedEduVanilla PlayStatusLoginFailedServerFull + PlayStatusLoginFailedEditorVanilla + PlayStatusLoginFailedVanillaEditor ) // PlayStatus is sent by the server to update a player on the play status. This includes failed statuses due diff --git a/minecraft/protocol/packet/pool.go b/minecraft/protocol/packet/pool.go index 6a1b55e2..9e7050d0 100644 --- a/minecraft/protocol/packet/pool.go +++ b/minecraft/protocol/packet/pool.go @@ -210,6 +210,10 @@ func init() { IDRequestAbility: func() Packet { return &RequestAbility{} }, IDRequestPermissions: func() Packet { return &RequestPermissions{} }, IDToastRequest: func() Packet { return &ToastRequest{} }, + IDUpdateAbilities: func() Packet { return &UpdateAbilities{} }, + IDUpdateAdventureSettings: func() Packet { return &UpdateAdventureSettings{} }, + IDDeathInfo: func() Packet { return &DeathInfo{} }, + IDEditorNetwork: func() Packet { return &EditorNetwork{} }, } for id, pk := range packets { Register(id, pk) diff --git a/minecraft/protocol/packet/position_tracking_db_server_broadcast.go b/minecraft/protocol/packet/position_tracking_db_server_broadcast.go index fbf273cb..e4c18bcf 100644 --- a/minecraft/protocol/packet/position_tracking_db_server_broadcast.go +++ b/minecraft/protocol/packet/position_tracking_db_server_broadcast.go @@ -1,6 +1,7 @@ package packet import ( + "github.com/sandertv/gophertunnel/minecraft/nbt" "github.com/sandertv/gophertunnel/minecraft/protocol" ) @@ -27,8 +28,7 @@ type PositionTrackingDBServerBroadcast struct { // TrackingID is the ID of the PositionTrackingDBClientRequest packet that this packet was in response to. // The tracking ID is also present as the 'id' field in the SerialisedData field. TrackingID int32 - // SerialisedData is a network little endian encoded NBT compound tag holding the data retrieved from the - // position tracking DB. + // Payload is a network little endian compound tag holding the data retrieved from the position tracking DB. // An example data structure sent if BroadcastAction is of the type Update: // TAG_Compound({ // 'version': TAG_Byte(0x01), @@ -41,7 +41,7 @@ type PositionTrackingDBServerBroadcast struct { // }), // 'status': TAG_Byte(0x00), // 0x00 for updating, 0x02 for not found/block destroyed. // }) - SerialisedData []byte + Payload map[string]any } // ID ... @@ -53,12 +53,12 @@ func (*PositionTrackingDBServerBroadcast) ID() uint32 { func (pk *PositionTrackingDBServerBroadcast) Marshal(w *protocol.Writer) { w.Uint8(&pk.BroadcastAction) w.Varint32(&pk.TrackingID) - w.Bytes(&pk.SerialisedData) + w.NBT(&pk.Payload, nbt.NetworkLittleEndian) } // Unmarshal ... func (pk *PositionTrackingDBServerBroadcast) Unmarshal(r *protocol.Reader) { r.Uint8(&pk.BroadcastAction) r.Varint32(&pk.TrackingID) - r.Bytes(&pk.SerialisedData) + r.NBT(&pk.Payload, nbt.NetworkLittleEndian) } diff --git a/minecraft/protocol/packet/request_ability.go b/minecraft/protocol/packet/request_ability.go index 52c7889e..10f36595 100644 --- a/minecraft/protocol/packet/request_ability.go +++ b/minecraft/protocol/packet/request_ability.go @@ -4,32 +4,11 @@ import ( "github.com/sandertv/gophertunnel/minecraft/protocol" ) -const ( - AbilityBuild = iota - AbilityMine - AbilityDoorsAndSwitches - AbilityOpenContainers - AbilityAttackPlayers - AbilityAttackMobs - AbilityOperatorCommands - AbilityTeleport - AbilityInvulnerable - AbilityFlying - AbilityMayFly - AbilityInstantBuild - AbilityLightning - AbilityFlySpeed - AbilityWalkSpeed - AbilityMuted - AbilityWorldBuilder - AbilityNoClip - AbilityAbilityCount -) - // RequestAbility is a packet sent by the client to the server to request permission for a specific ability from the // server. These abilities are defined above. type RequestAbility struct { - // Ability is the ability that the client is requesting. This is one of the constants defined above. + // Ability is the ability that the client is requesting. This is one of the constants defined in the + // protocol/ability.go file. Ability int32 // Value represents the value of the ability. This can either be a boolean or a float32, otherwise the writer/reader // will panic. diff --git a/minecraft/protocol/packet/start_game.go b/minecraft/protocol/packet/start_game.go index 03698bc0..6212fca1 100644 --- a/minecraft/protocol/packet/start_game.go +++ b/minecraft/protocol/packet/start_game.go @@ -66,6 +66,9 @@ type StartGame struct { // value is set to true while the player's or the world's game mode is creative, and it's recommended to // simply always set this to false as a server. AchievementsDisabled bool + // EditorWorld is a value to dictate if the world is in editor mode, a special mode recently introduced adding + // "powerful tools for editing worlds, intended for experienced creators." + EditorWorld bool // DayCycleLockTime is the time at which the day cycle was locked if the day cycle is disabled using the // respective game rule. The client will maintain this time as long as the day cycle is disabled. DayCycleLockTime int32 @@ -122,7 +125,7 @@ type StartGame struct { StartWithMapEnabled bool // PlayerPermissions is the permission level of the player. It is a value from 0-3, with 0 being visitor, // 1 being member, 2 being operator and 3 being custom. - PlayerPermissions int32 + PlayerPermissions uint8 // ServerChunkTickRadius is the radius around the player in which chunks are ticked. Most servers set this // value to a fixed number, as it does not necessarily affect anything client-side. ServerChunkTickRadius int32 @@ -229,6 +232,7 @@ func (pk *StartGame) Marshal(w *protocol.Writer) { w.Varint32(&pk.Difficulty) w.UBlockPos(&pk.WorldSpawn) w.Bool(&pk.AchievementsDisabled) + w.Bool(&pk.EditorWorld) w.Varint32(&pk.DayCycleLockTime) w.Varint32(&pk.EducationEditionOffer) w.Bool(&pk.EducationFeaturesEnabled) @@ -251,7 +255,7 @@ func (pk *StartGame) Marshal(w *protocol.Writer) { w.Bool(&pk.ExperimentsPreviouslyToggled) w.Bool(&pk.BonusChestEnabled) w.Bool(&pk.StartWithMapEnabled) - w.Varint32(&pk.PlayerPermissions) + w.Uint8(&pk.PlayerPermissions) w.Int32(&pk.ServerChunkTickRadius) w.Bool(&pk.HasLockedBehaviourPack) w.Bool(&pk.HasLockedTexturePack) @@ -316,6 +320,7 @@ func (pk *StartGame) Unmarshal(r *protocol.Reader) { r.Varint32(&pk.Difficulty) r.UBlockPos(&pk.WorldSpawn) r.Bool(&pk.AchievementsDisabled) + r.Bool(&pk.EditorWorld) r.Varint32(&pk.DayCycleLockTime) r.Varint32(&pk.EducationEditionOffer) r.Bool(&pk.EducationFeaturesEnabled) @@ -339,7 +344,7 @@ func (pk *StartGame) Unmarshal(r *protocol.Reader) { r.Bool(&pk.ExperimentsPreviouslyToggled) r.Bool(&pk.BonusChestEnabled) r.Bool(&pk.StartWithMapEnabled) - r.Varint32(&pk.PlayerPermissions) + r.Uint8(&pk.PlayerPermissions) r.Int32(&pk.ServerChunkTickRadius) r.Bool(&pk.HasLockedBehaviourPack) r.Bool(&pk.HasLockedTexturePack) diff --git a/minecraft/protocol/packet/update_abilities.go b/minecraft/protocol/packet/update_abilities.go new file mode 100644 index 00000000..e07369bf --- /dev/null +++ b/minecraft/protocol/packet/update_abilities.go @@ -0,0 +1,53 @@ +package packet + +import ( + "github.com/sandertv/gophertunnel/minecraft/protocol" +) + +// UpdateAbilities is a packet sent from the server to the client to update the abilities of the player. It, along with +// the UpdateAdventureSettings packet, are replacements of the AdventureSettings packet since v1.19.10. +type UpdateAbilities struct { + // EntityUniqueID is the unique ID of the player. 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 + // PlayerPermissions is the permission level of the player. It is a value from 0-3, with 0 being visitor, + // 1 being member, 2 being operator and 3 being custom. + PlayerPermissions uint8 + // CommandPermissions is a permission level that specifies the kind of commands that the player is + // allowed to use. It is one of the CommandPermissionLevel constants in the AdventureSettings packet. + CommandPermissions uint8 + // Layers contains all ability layers and their potential values. This should at least have one entry, being the + // base layer. + Layers []protocol.AbilityLayer +} + +// ID ... +func (*UpdateAbilities) ID() uint32 { + return IDUpdateAbilities +} + +// Marshal ... +func (pk *UpdateAbilities) Marshal(w *protocol.Writer) { + w.Int64(&pk.EntityUniqueID) + w.Uint8(&pk.PlayerPermissions) + w.Uint8(&pk.CommandPermissions) + layersLen := uint8(len(pk.Layers)) + w.Uint8(&layersLen) + for _, layer := range pk.Layers { + protocol.SerializedLayer(w, &layer) + } +} + +// Unmarshal ... +func (pk *UpdateAbilities) Unmarshal(r *protocol.Reader) { + r.Int64(&pk.EntityUniqueID) + r.Uint8(&pk.PlayerPermissions) + r.Uint8(&pk.CommandPermissions) + var layersLen uint8 + r.Uint8(&layersLen) + pk.Layers = make([]protocol.AbilityLayer, layersLen) + for i := uint8(0); i < layersLen; i++ { + protocol.SerializedLayer(r, &pk.Layers[i]) + } +} diff --git a/minecraft/protocol/packet/update_adventure_settings.go b/minecraft/protocol/packet/update_adventure_settings.go new file mode 100644 index 00000000..209484a7 --- /dev/null +++ b/minecraft/protocol/packet/update_adventure_settings.go @@ -0,0 +1,44 @@ +package packet + +import ( + "github.com/sandertv/gophertunnel/minecraft/protocol" +) + +// UpdateAdventureSettings is a packet sent from the server to the client to update the adventure settings of the player. +// It, along with the UpdateAbilities packet, are replacements of the AdventureSettings packet since v1.19.10. +type UpdateAdventureSettings struct { + // NoPvM is a boolean indicating whether the player is allowed to fight mobs or not. + NoPvM bool + // NoMvP is a boolean indicating whether mobs are allowed to fight the player or not. It is unclear why this is sent + // to the client. + NoMvP bool + // ImmutableWorld is a boolean indicating whether the player is allowed to modify the world or not. + ImmutableWorld bool + // ShowNameTags is a boolean indicating whether player name tags are shown or not. + ShowNameTags bool + // AutoJump is a boolean indicating whether the player is allowed to jump automatically or not. + AutoJump bool +} + +// ID ... +func (*UpdateAdventureSettings) ID() uint32 { + return IDUpdateAdventureSettings +} + +// Marshal ... +func (pk *UpdateAdventureSettings) Marshal(w *protocol.Writer) { + w.Bool(&pk.NoPvM) + w.Bool(&pk.NoMvP) + w.Bool(&pk.ImmutableWorld) + w.Bool(&pk.ShowNameTags) + w.Bool(&pk.AutoJump) +} + +// Unmarshal ... +func (pk *UpdateAdventureSettings) Unmarshal(r *protocol.Reader) { + r.Bool(&pk.NoPvM) + r.Bool(&pk.NoMvP) + r.Bool(&pk.ImmutableWorld) + r.Bool(&pk.ShowNameTags) + r.Bool(&pk.AutoJump) +}