diff --git a/minecraft/protocol/events.go b/minecraft/protocol/events.go index 3e10723f..edf784c8 100644 --- a/minecraft/protocol/events.go +++ b/minecraft/protocol/events.go @@ -33,6 +33,7 @@ const ( EventTypeStriderRiddenInLavaInOverworld EventTypeSneakCloseToSculkSensor EventTypeCarefulRestoration + EventTypeItemUsed ) // lookupEvent looks up an Event matching the event type passed. False is @@ -87,6 +88,8 @@ func lookupEvent(eventType int32, x *Event) bool { *x = &WaxedOrUnwaxedCopperEvent{} case EventTypeSneakCloseToSculkSensor: *x = &SneakCloseToSculkSensorEvent{} + case EventTypeItemUsed: + *x = &ItemUsedEvent{} default: return false } @@ -144,6 +147,8 @@ func lookupEventType(x Event, eventType *int32) bool { *eventType = EventTypePlayerWaxedOrUnwaxedCopper case *SneakCloseToSculkSensorEvent: *eventType = EventTypeSneakCloseToSculkSensor + case *ItemUsedEvent: + *eventType = EventTypeItemUsed default: return false } @@ -567,3 +572,18 @@ type SneakCloseToSculkSensorEvent struct{} // Marshal ... func (u *SneakCloseToSculkSensorEvent) Marshal(r IO) {} + +type ItemUsedEvent struct { + ItemID int16 + ItemAux int32 + UseMethod int32 + UseCount int32 +} + +// Marshal ... +func (i *ItemUsedEvent) Marshal(r IO) { + r.Int16(&i.ItemID) + r.Varint32(&i.ItemAux) + r.Varint32(&i.UseMethod) + r.Varint32(&i.UseCount) +} diff --git a/minecraft/protocol/info.go b/minecraft/protocol/info.go index 6925a448..522f99ab 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 = 671 + CurrentProtocol = 685 // CurrentVersion is the current version of Minecraft as supported by the `packet` package. - CurrentVersion = "1.20.80" + CurrentVersion = "1.21.0" ) diff --git a/minecraft/protocol/packet/award_achievement.go b/minecraft/protocol/packet/award_achievement.go new file mode 100644 index 00000000..0da85bfc --- /dev/null +++ b/minecraft/protocol/packet/award_achievement.go @@ -0,0 +1,21 @@ +package packet + +import ( + "github.com/sandertv/gophertunnel/minecraft/protocol" +) + +// AwardAchievement is sent by the server to award an achievement to a player. +type AwardAchievement struct { + // AchievementID is the ID of the achievement that should be awarded to the player. The values for these + // IDs are currently unknown. + AchievementID int32 +} + +// ID ... +func (*AwardAchievement) ID() uint32 { + return IDAwardAchievement +} + +func (pk *AwardAchievement) Marshal(io protocol.IO) { + io.Int32(&pk.AchievementID) +} diff --git a/minecraft/protocol/packet/code_builder_source.go b/minecraft/protocol/packet/code_builder_source.go index 4df6542c..8bd11fcf 100644 --- a/minecraft/protocol/packet/code_builder_source.go +++ b/minecraft/protocol/packet/code_builder_source.go @@ -4,6 +4,13 @@ import ( "github.com/sandertv/gophertunnel/minecraft/protocol" ) +const ( + CodeBuilderOperationNone = iota + CodeBuilderOperationGet + CodeBuilderOperationSet + CodeBuilderOperationReset +) + const ( CodeBuilderCategoryNone = iota CodeBuilderCategoryStatus @@ -11,10 +18,12 @@ const ( ) const ( - CodeBuilderOperationNone = iota - CodeBuilderOperationGet - CodeBuilderOperationSet - CodeBuilderOperationReset + CodeBuilderStatusNone = iota + CodeBuilderStatusNotStarted + CodeBuilderStatusInProgress + CodeBuilderStatusPaused + CodeBuilderStatusError + CodeBuilderStatusSucceeded ) // CodeBuilderSource is an Education Edition packet sent by the client to the server to run an operation with a @@ -25,9 +34,8 @@ type CodeBuilderSource struct { // Category is used to distinguish the category of the operation performed. It is always one of the constants // listed above. Category byte - // Value contains extra data about the operation performed. It is always empty unless the operation is - // CodeBuilderOperationSet. - Value []byte + // CodeStatus is the status of the code builder. It is always one of the constants listed above. + CodeStatus byte } // ID ... @@ -38,5 +46,5 @@ func (pk *CodeBuilderSource) ID() uint32 { func (pk *CodeBuilderSource) Marshal(io protocol.IO) { io.Uint8(&pk.Operation) io.Uint8(&pk.Category) - io.ByteSlice(&pk.Value) + io.Uint8(&pk.CodeStatus) } diff --git a/minecraft/protocol/packet/completed_using_item.go b/minecraft/protocol/packet/completed_using_item.go index a0de3f25..4d5753e8 100644 --- a/minecraft/protocol/packet/completed_using_item.go +++ b/minecraft/protocol/packet/completed_using_item.go @@ -20,6 +20,8 @@ const ( UseItemRetrieved UseItemDyed UseItemTraded + UseItemBrushingCompleted + UseItemOpenedVault ) // CompletedUsingItem is sent by the server to tell the client that it should be done using the item it is diff --git a/minecraft/protocol/packet/container_close.go b/minecraft/protocol/packet/container_close.go index 3b374577..0a99399d 100644 --- a/minecraft/protocol/packet/container_close.go +++ b/minecraft/protocol/packet/container_close.go @@ -11,6 +11,9 @@ type ContainerClose struct { // WindowID is the ID representing the window of the container that should be closed. It must be equal to // the one sent in the ContainerOpen packet to close the designated window. WindowID byte + // ContainerType is the type of container that the server is trying to close. This is used to validate on + // the client side whether or not the server's close request is valid. + ContainerType byte // ServerSide determines whether or not the container was force-closed by the server. If this value is // not set correctly, the client may ignore the packet and respond with a PacketViolationWarning. ServerSide bool @@ -23,5 +26,6 @@ func (*ContainerClose) ID() uint32 { func (pk *ContainerClose) Marshal(io protocol.IO) { io.Uint8(&pk.WindowID) + io.Uint8(&pk.ContainerType) io.Bool(&pk.ServerSide) } diff --git a/minecraft/protocol/packet/id.go b/minecraft/protocol/packet/id.go index 419a54e4..9468e2da 100644 --- a/minecraft/protocol/packet/id.go +++ b/minecraft/protocol/packet/id.go @@ -209,4 +209,5 @@ const ( IDPlayerToggleCrafterSlotRequest IDSetPlayerInventoryOptions IDSetHud + IDAwardAchievement ) diff --git a/minecraft/protocol/packet/level_event.go b/minecraft/protocol/packet/level_event.go index 7f9a6f3c..1d6706c9 100644 --- a/minecraft/protocol/packet/level_event.go +++ b/minecraft/protocol/packet/level_event.go @@ -7,130 +7,134 @@ import ( // noinspection SpellCheckingInspection const ( - LevelEventSoundClick = 1000 - LevelEventSoundClickFail = 1001 - LevelEventSoundLaunch = 1002 - LevelEventSoundOpenDoor = 1003 - LevelEventSoundFizz = 1004 - LevelEventSoundFuse = 1005 - LevelEventSoundPlayRecording = 1006 - LevelEventSoundGhastWarning = 1007 - LevelEventSoundGhastFireball = 1008 - LevelEventSoundBlazeFireball = 1009 - LevelEventSoundZombieWoodenDoor = 1010 - LevelEventSoundZombieDoorCrash = 1012 - LevelEventSoundZombieInfected = 1016 - LevelEventSoundZombieConverted = 1017 - LevelEventSoundEndermanTeleport = 1018 - LevelEventSoundAnvilBroken = 1020 - LevelEventSoundAnvilUsed = 1021 - LevelEventSoundAnvilLand = 1022 - LevelEventSoundInfinityArrowPickup = 1030 - LevelEventSoundTeleportEnderPearl = 1032 - LevelEventSoundAddItem = 1040 - LevelEventSoundItemFrameBreak = 1041 - LevelEventSoundItemFramePlace = 1042 - LevelEventSoundItemFrameRemoveItem = 1043 - LevelEventSoundItemFrameRotateItem = 1044 - LevelEventSoundExperienceOrbPickup = 1051 - LevelEventSoundTotemUsed = 1052 - LevelEventSoundArmorStandBreak = 1060 - LevelEventSoundArmorStandHit = 1061 - LevelEventSoundArmorStandLand = 1062 - LevelEventSoundArmorStandPlace = 1063 - LevelEventSoundPointedDripstoneLand = 1064 - LevelEventSoundDyeUsed = 1065 - LevelEventSoundInkSacUsed = 1066 - LevelEventSoundAmethystResonate = 1067 - LevelEventQueueCustomMusic = 1900 - LevelEventPlayCustomMusic = 1901 - LevelEventStopCustomMusic = 1902 - LevelEventSetMusicVolume = 1903 - LevelEventParticlesShoot = 2000 - LevelEventParticlesDestroyBlock = 2001 - LevelEventParticlesPotionSplash = 2002 - LevelEventParticlesEyeOfEnderDeath = 2003 - LevelEventParticlesMobBlockSpawn = 2004 - LevelEventParticleCropGrowth = 2005 - LevelEventParticleSoundGuardianGhost = 2006 - LevelEventParticleDeathSmoke = 2007 - LevelEventParticleDenyBlock = 2008 - LevelEventParticleGenericSpawn = 2009 - LevelEventParticlesDragonEgg = 2010 - LevelEventParticlesCropEaten = 2011 - LevelEventParticlesCritical = 2012 - LevelEventParticlesTeleport = 2013 - LevelEventParticlesCrackBlock = 2014 - LevelEventParticlesBubble = 2015 - LevelEventParticlesEvaporate = 2016 - LevelEventParticlesDestroyArmorStand = 2017 - LevelEventParticlesBreakingEgg = 2018 - LevelEventParticleDestroyEgg = 2019 - LevelEventParticlesEvaporateWater = 2020 - LevelEventParticlesDestroyBlockNoSound = 2021 - LevelEventParticlesKnockbackRoar = 2022 - LevelEventParticlesTeleportTrail = 2023 - LevelEventParticlesPointCloud = 2024 - LevelEventParticlesExplosion = 2025 - LevelEventParticlesBlockExplosion = 2026 - LevelEventParticlesVibrationSignal = 2027 - LevelEventParticlesDripstoneDrip = 2028 - LevelEventParticlesFizzEffect = 2029 - LevelEventWaxOn = 2030 - LevelEventWaxOff = 2031 - LevelEventScrape = 2032 - LevelEventParticlesElectricSpark = 2033 - LevelEventParticleTurtleEgg = 2034 - LevelEventParticleSculkShriek = 2035 - LevelEventSculkCatalystBloom = 2036 - LevelEventSculkCharge = 2037 - LevelEventSculkChargePop = 2038 - LevelEventSonicExplosion = 2039 - LevelEventDustPlume = 2040 - LevelEventStartRaining = 3001 - LevelEventStartThunderstorm = 3002 - LevelEventStopRaining = 3003 - LevelEventStopThunderstorm = 3004 - LevelEventGlobalPause = 3005 - LevelEventSimTimeStep = 3006 - LevelEventSimTimeScale = 3007 - LevelEventActivateBlock = 3500 - LevelEventCauldronExplode = 3501 - LevelEventCauldronDyeArmor = 3502 - LevelEventCauldronCleanArmor = 3503 - LevelEventCauldronFillPotion = 3504 - LevelEventCauldronTakePotion = 3505 - LevelEventCauldronFillWater = 3506 - LevelEventCauldronTakeWater = 3507 - LevelEventCauldronAddDye = 3508 - LevelEventCauldronCleanBanner = 3509 - LevelEventCauldronFlush = 3510 - LevelEventAgentSpawnEffect = 3511 - LevelEventCauldronFillLava = 3512 - LevelEventCauldronTakeLava = 3513 - LevelEventCauldronFillPowderSnow = 3514 - LevelEventCauldronTakePowderSnow = 3515 - LevelEventStartBlockCracking = 3600 - LevelEventStopBlockCracking = 3601 - LevelEventUpdateBlockCracking = 3602 - LevelEventParticlesCrackBlockDown = 3603 - LevelEventParticlesCrackBlockUp = 3604 - LevelEventParticlesCrackBlockNorth = 3605 - LevelEventParticlesCrackBlockSouth = 3606 - LevelEventParticlesCrackBlockWest = 3607 - LevelEventParticlesCrackBlockEast = 3608 - LevelEventParticlesShootWhiteSmoke = 3609 - LevelEventParticlesWindExplosion = 3610 - LevelEventParticlesTrialSpawnerDetection = 3611 - LevelEventParticlesTrialSpawnerSpawning = 3612 - LevelEventParticlesTrialSpawnerEjecting = 3613 - LevelEventAllPlayersSleeping = 9800 - LevelEventSleepingPlayers = 9801 - LevelEventJumpPrevented = 9810 - LevelEventAnimationVaultActivate = 9811 - LevelEventAnimationVaultDeactivate = 9812 - LevelEventAnimationVaultEjectItem = 9813 - LevelEventParticleLegacyEvent = 0x4000 + LevelEventSoundClick = 1000 + LevelEventSoundClickFail = 1001 + LevelEventSoundLaunch = 1002 + LevelEventSoundOpenDoor = 1003 + LevelEventSoundFizz = 1004 + LevelEventSoundFuse = 1005 + LevelEventSoundPlayRecording = 1006 + LevelEventSoundGhastWarning = 1007 + LevelEventSoundGhastFireball = 1008 + LevelEventSoundBlazeFireball = 1009 + LevelEventSoundZombieWoodenDoor = 1010 + LevelEventSoundZombieDoorCrash = 1012 + LevelEventSoundZombieInfected = 1016 + LevelEventSoundZombieConverted = 1017 + LevelEventSoundEndermanTeleport = 1018 + LevelEventSoundAnvilBroken = 1020 + LevelEventSoundAnvilUsed = 1021 + LevelEventSoundAnvilLand = 1022 + LevelEventSoundInfinityArrowPickup = 1030 + LevelEventSoundTeleportEnderPearl = 1032 + LevelEventSoundAddItem = 1040 + LevelEventSoundItemFrameBreak = 1041 + LevelEventSoundItemFramePlace = 1042 + LevelEventSoundItemFrameRemoveItem = 1043 + LevelEventSoundItemFrameRotateItem = 1044 + LevelEventSoundExperienceOrbPickup = 1051 + LevelEventSoundTotemUsed = 1052 + LevelEventSoundArmorStandBreak = 1060 + LevelEventSoundArmorStandHit = 1061 + LevelEventSoundArmorStandLand = 1062 + LevelEventSoundArmorStandPlace = 1063 + LevelEventSoundPointedDripstoneLand = 1064 + LevelEventSoundDyeUsed = 1065 + LevelEventSoundInkSacUsed = 1066 + LevelEventSoundAmethystResonate = 1067 + LevelEventQueueCustomMusic = 1900 + LevelEventPlayCustomMusic = 1901 + LevelEventStopCustomMusic = 1902 + LevelEventSetMusicVolume = 1903 + LevelEventParticlesShoot = 2000 + LevelEventParticlesDestroyBlock = 2001 + LevelEventParticlesPotionSplash = 2002 + LevelEventParticlesEyeOfEnderDeath = 2003 + LevelEventParticlesMobBlockSpawn = 2004 + LevelEventParticleCropGrowth = 2005 + LevelEventParticleSoundGuardianGhost = 2006 + LevelEventParticleDeathSmoke = 2007 + LevelEventParticleDenyBlock = 2008 + LevelEventParticleGenericSpawn = 2009 + LevelEventParticlesDragonEgg = 2010 + LevelEventParticlesCropEaten = 2011 + LevelEventParticlesCritical = 2012 + LevelEventParticlesTeleport = 2013 + LevelEventParticlesCrackBlock = 2014 + LevelEventParticlesBubble = 2015 + LevelEventParticlesEvaporate = 2016 + LevelEventParticlesDestroyArmorStand = 2017 + LevelEventParticlesBreakingEgg = 2018 + LevelEventParticleDestroyEgg = 2019 + LevelEventParticlesEvaporateWater = 2020 + LevelEventParticlesDestroyBlockNoSound = 2021 + LevelEventParticlesKnockbackRoar = 2022 + LevelEventParticlesTeleportTrail = 2023 + LevelEventParticlesPointCloud = 2024 + LevelEventParticlesExplosion = 2025 + LevelEventParticlesBlockExplosion = 2026 + LevelEventParticlesVibrationSignal = 2027 + LevelEventParticlesDripstoneDrip = 2028 + LevelEventParticlesFizzEffect = 2029 + LevelEventWaxOn = 2030 + LevelEventWaxOff = 2031 + LevelEventScrape = 2032 + LevelEventParticlesElectricSpark = 2033 + LevelEventParticleTurtleEgg = 2034 + LevelEventParticleSculkShriek = 2035 + LevelEventSculkCatalystBloom = 2036 + LevelEventSculkCharge = 2037 + LevelEventSculkChargePop = 2038 + LevelEventSonicExplosion = 2039 + LevelEventDustPlume = 2040 + LevelEventStartRaining = 3001 + LevelEventStartThunderstorm = 3002 + LevelEventStopRaining = 3003 + LevelEventStopThunderstorm = 3004 + LevelEventGlobalPause = 3005 + LevelEventSimTimeStep = 3006 + LevelEventSimTimeScale = 3007 + LevelEventActivateBlock = 3500 + LevelEventCauldronExplode = 3501 + LevelEventCauldronDyeArmor = 3502 + LevelEventCauldronCleanArmor = 3503 + LevelEventCauldronFillPotion = 3504 + LevelEventCauldronTakePotion = 3505 + LevelEventCauldronFillWater = 3506 + LevelEventCauldronTakeWater = 3507 + LevelEventCauldronAddDye = 3508 + LevelEventCauldronCleanBanner = 3509 + LevelEventCauldronFlush = 3510 + LevelEventAgentSpawnEffect = 3511 + LevelEventCauldronFillLava = 3512 + LevelEventCauldronTakeLava = 3513 + LevelEventCauldronFillPowderSnow = 3514 + LevelEventCauldronTakePowderSnow = 3515 + LevelEventStartBlockCracking = 3600 + LevelEventStopBlockCracking = 3601 + LevelEventUpdateBlockCracking = 3602 + LevelEventParticlesCrackBlockDown = 3603 + LevelEventParticlesCrackBlockUp = 3604 + LevelEventParticlesCrackBlockNorth = 3605 + LevelEventParticlesCrackBlockSouth = 3606 + LevelEventParticlesCrackBlockWest = 3607 + LevelEventParticlesCrackBlockEast = 3608 + LevelEventParticlesShootWhiteSmoke = 3609 + LevelEventParticlesBreezeWindExplosion = 3610 + LevelEventParticlesTrialSpawnerDetection = 3611 + LevelEventParticlesTrialSpawnerSpawning = 3612 + LevelEventParticlesTrialSpawnerEjecting = 3613 + LevelEventParticlesWindExplosion = 3614 + LevelEventParticlesTrialSpawnerDetectionCharged = 3615 + LevelEventParticlesTrialSpawnerBecomeCharged = 3616 + LevelEventAllPlayersSleeping = 9800 + LevelEventSleepingPlayers = 9801 + LevelEventJumpPrevented = 9810 + LevelEventAnimationVaultActivate = 9811 + LevelEventAnimationVaultDeactivate = 9812 + LevelEventAnimationVaultEjectItem = 9813 + LevelEventAnimationSpawnCobweb = 9814 + LevelEventParticleLegacyEvent = 0x4000 ) // LevelEvent is sent by the server to make a certain event in the level occur. It ranges from particles, to diff --git a/minecraft/protocol/packet/level_sound_event.go b/minecraft/protocol/packet/level_sound_event.go index dbf39c79..b2fefc96 100644 --- a/minecraft/protocol/packet/level_sound_event.go +++ b/minecraft/protocol/packet/level_sound_event.go @@ -493,6 +493,50 @@ const ( SoundEventCrafterDisableSlot SoundEventCopperBulbTurnOn SoundEventCopperBulbTurnOff + SoundEventAmbientInAir + SoundEventBreezeWindChargeBurst + SoundEventImitateBreeze + SoundEventArmadilloBrush + SoundEventArmadilloScuteDrop + SoundEventEquipWolf + SoundEventUnequipWolf + SoundEventReflect + SoundEventVaultOpenShutter + SoundEventVaultCloseShutter + SoundEventVaultEjectItem + SoundEventVaultInsertItem + SoundEventVaultInsertItemFail + SoundEventVaultAmbient + SoundEventVaultActivate + SoundEventVaultDeactive + SoundEventHurtReduced + SoundEventWindChargeBurst + SoundEventImitateBogged + SoundEventWolfArmourCrack + SoundEventWolfArmourBreak + SoundEventWolfArmourRepair + SoundEventMaceSmashAir + SoundEventMaceSmashGround + SoundEventTrialSpawnerChargeActivate + SoundEventTrialSpawnerAmbientOminous + SoundEventOminiousItemSpawnerSpawnItem + SoundEventOminousBottleEndUse + SoundEventMaceHeavySmashGround + SoundEventOminousItemSpawnerSpawnItemBegin + _ + _ + _ + _ + _ + _ + _ + SoundEventApplyEffectBadOmen + SoundEventApplyEffectRaidOmen + SoundEventApplyEffectTrialOmen + OminousItemSpawnerAboutToSpawnItem + SoundEventRecordCreator + SoundEventRecordCreatorMusicBox + SoundEventRecordPrecipice ) // LevelSoundEvent is sent by the server to make any kind of built-in sound heard to a player. It is sent to, diff --git a/minecraft/protocol/packet/player_auth_input.go b/minecraft/protocol/packet/player_auth_input.go index bfd71aa4..b70fec1b 100644 --- a/minecraft/protocol/packet/player_auth_input.go +++ b/minecraft/protocol/packet/player_auth_input.go @@ -54,6 +54,7 @@ const ( InputFlagClientPredictedVehicle InputFlagPaddlingLeft InputFlagPaddlingRight + InputFlagBlockBreakingDelayEnabled ) const ( diff --git a/minecraft/protocol/packet/pool.go b/minecraft/protocol/packet/pool.go index 73d94826..464918bc 100644 --- a/minecraft/protocol/packet/pool.go +++ b/minecraft/protocol/packet/pool.go @@ -250,6 +250,7 @@ func init() { IDPlayerToggleCrafterSlotRequest: func() Packet { return &PlayerToggleCrafterSlotRequest{} }, IDSetPlayerInventoryOptions: func() Packet { return &SetPlayerInventoryOptions{} }, IDSetHud: func() Packet { return &SetHud{} }, + IDAwardAchievement: func() Packet { return &AwardAchievement{} }, } for id, pk := range serverOriginating { RegisterPacketFromServer(id, pk) diff --git a/minecraft/protocol/packet/start_game.go b/minecraft/protocol/packet/start_game.go index eeb6d70c..f20d117b 100644 --- a/minecraft/protocol/packet/start_game.go +++ b/minecraft/protocol/packet/start_game.go @@ -241,6 +241,12 @@ type StartGame struct { ChatRestrictionLevel uint8 // DisablePlayerInteractions is true if the client should ignore other players when interacting with the world. DisablePlayerInteractions bool + // ServerID is always empty in vanilla and its usage is currently unknown. + ServerID string + // WorldID is always empty in vanilla and its usage is currently unknown. + WorldID string + // ScenarioID is always empty in vanilla and its usage is currently unknown. + ScenarioID string // UseBlockNetworkIDHashes is true if the client should use the hash of a block's name as its network ID rather than // 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. @@ -312,6 +318,9 @@ func (pk *StartGame) Marshal(io protocol.IO) { protocol.OptionalFunc(io, &pk.ForceExperimentalGameplay, io.Bool) io.Uint8(&pk.ChatRestrictionLevel) io.Bool(&pk.DisablePlayerInteractions) + io.String(&pk.ServerID) + io.String(&pk.WorldID) + io.String(&pk.ScenarioID) io.String(&pk.LevelID) io.String(&pk.WorldName) io.String(&pk.TemplateContentIdentity) diff --git a/minecraft/protocol/packet/text.go b/minecraft/protocol/packet/text.go index 91b6a9e7..4ffc2c6f 100644 --- a/minecraft/protocol/packet/text.go +++ b/minecraft/protocol/packet/text.go @@ -46,6 +46,8 @@ type Text struct { // Nintendo Switch). It is otherwise an empty string, and is used to decide which players are able to // chat with each other. PlatformChatID string + // FilteredMessage is always set to empty and the usage is currently unknown. + FilteredMessage string } // ID ... @@ -68,4 +70,5 @@ func (pk *Text) Marshal(io protocol.IO) { } io.String(&pk.XUID) io.String(&pk.PlatformChatID) + io.String(&pk.FilteredMessage) } diff --git a/minecraft/protocol/recipe.go b/minecraft/protocol/recipe.go index 762afd4c..73ac8e3d 100644 --- a/minecraft/protocol/recipe.go +++ b/minecraft/protocol/recipe.go @@ -56,6 +56,30 @@ func (x *PotionRecipe) Marshal(r IO) { r.Varint32(&x.OutputPotionMetadata) } +const ( + RecipeUnlockContextNone = iota + RecipeUnlockContextAlwaysUnlocked + RecipeUnlockContextPlayerInWater + RecipeUnlockContextPlayerHasManyItems +) + +// RecipeUnlockRequirement represents a requirement that must be met in order to unlock a recipe. This is used +// for both shaped and shapeless recipes. +type RecipeUnlockRequirement struct { + // Context is the context in which the recipe is unlocked. This is one of the constants above. + Context byte + // Ingredients are the ingredients required to unlock the recipe and only used if Context is set to none. + Ingredients []ItemDescriptorCount +} + +// Marshal ... +func (x *RecipeUnlockRequirement) Marshal(r IO) { + r.Uint8(&x.Context) + if x.Context == RecipeUnlockContextNone { + FuncSlice(r, &x.Ingredients, r.ItemDescriptorCount) + } +} + const ( RecipeShapeless int32 = iota RecipeShaped @@ -164,6 +188,8 @@ type ShapelessRecipe struct { Block string // Priority ... Priority int32 + // UnlockRequirement is a requirement that must be met in order to unlock the recipe. + UnlockRequirement RecipeUnlockRequirement // RecipeNetworkID is a unique ID used to identify the recipe over network. Each recipe must have a unique // network ID. Recommended is to just increment a variable for each unique recipe registered. // This field must never be 0. @@ -208,6 +234,8 @@ type ShapedRecipe struct { // AssumeSymmetry specifies if the recipe is symmetrical. If this is set to true, the recipe will be // mirrored along the diagonal axis. This means that the recipe will be the same if rotated 180 degrees. AssumeSymmetry bool + // UnlockRequirement is a requirement that must be met in order to unlock the recipe. + UnlockRequirement RecipeUnlockRequirement // RecipeNetworkID is a unique ID used to identify the recipe over network. Each recipe must have a unique // network ID. Recommended is to just increment a variable for each unique recipe registered. // This field must never be 0. @@ -440,6 +468,7 @@ func marshalShaped(r IO, recipe *ShapedRecipe) { r.String(&recipe.Block) r.Varint32(&recipe.Priority) r.Bool(&recipe.AssumeSymmetry) + Single(r, &recipe.UnlockRequirement) r.Varuint32(&recipe.RecipeNetworkID) } @@ -451,5 +480,6 @@ func marshalShapeless(r IO, recipe *ShapelessRecipe) { r.UUID(&recipe.UUID) r.String(&recipe.Block) r.Varint32(&recipe.Priority) + Single(r, &recipe.UnlockRequirement) r.Varuint32(&recipe.RecipeNetworkID) }