This commit is contained in:
AkmalFairuz
2025-01-10 19:05:23 +07:00
21 changed files with 7935 additions and 26 deletions

View File

@@ -11,7 +11,7 @@ A gophertunnel protocol interface implementation to support older Minecraft Bedr
| 712 | 1.21.20 | ✅ | | 712 | 1.21.20 | ✅ |
| 686 | 1.21.2 | ✅ | | 686 | 1.21.2 | ✅ |
| 685 | 1.21.0 | ✅ | | 685 | 1.21.0 | ✅ |
| 671 | 1.20.80 | 🚧 | | 671 | 1.20.80 | |
| 662 | 1.20.70 | 🚧 | | 662 | 1.20.70 | 🚧 |
| 649 | 1.20.60 | 🚧 | | 649 | 1.20.60 | 🚧 |
| 630 | 1.20.50 | 🚧 | | 630 | 1.20.50 | 🚧 |

View File

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

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,32 @@
package legacypacket
import (
"github.com/akmalfairuz/legacy-version/legacyver/proto"
"github.com/sandertv/gophertunnel/minecraft/protocol"
"github.com/sandertv/gophertunnel/minecraft/protocol/packet"
)
// CodeBuilderSource is an Education Edition packet sent by the client to the server to run an operation with a
// code builder.
type CodeBuilderSource struct {
// Operation is used to distinguish the operation performed. It is always one of the constants listed above.
Operation byte
// Category is used to distinguish the category of the operation performed. It is always one of the constants
// listed above.
Category byte
// CodeStatus is the status of the code builder. It is always one of the constants listed above.
CodeStatus byte
}
// ID ...
func (pk *CodeBuilderSource) ID() uint32 {
return packet.IDCodeBuilderSource
}
func (pk *CodeBuilderSource) Marshal(io protocol.IO) {
io.Uint8(&pk.Operation)
io.Uint8(&pk.Category)
if proto.IsProtoGTE(io, proto.ID685) {
io.Uint8(&pk.CodeStatus)
}
}

View File

@@ -0,0 +1,35 @@
package legacypacket
import (
"github.com/akmalfairuz/legacy-version/legacyver/proto"
"github.com/sandertv/gophertunnel/minecraft/protocol"
"github.com/sandertv/gophertunnel/minecraft/protocol/packet"
)
// ContainerClose is sent by the server to close a container the player currently has opened, which was opened
// using the ContainerOpen packet, or by the client to tell the server it closed a particular container, such
// as the crafting grid.
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
}
// ID ...
func (*ContainerClose) ID() uint32 {
return packet.IDContainerClose
}
func (pk *ContainerClose) Marshal(io protocol.IO) {
io.Uint8(&pk.WindowID)
if proto.IsProtoGTE(io, proto.ID685) {
io.Uint8(&pk.ContainerType)
}
io.Bool(&pk.ServerSide)
}

View File

@@ -0,0 +1,42 @@
package legacypacket
import (
"github.com/akmalfairuz/legacy-version/legacyver/proto"
"github.com/sandertv/gophertunnel/minecraft/protocol"
"github.com/sandertv/gophertunnel/minecraft/protocol/packet"
)
// CraftingData is sent by the server to let the client know all crafting data that the server maintains. This
// includes shapeless crafting, crafting table recipes, furnace recipes etc. Each crafting station's recipes
// are included in it.
type CraftingData struct {
// Recipes is a list of all recipes available on the server. It includes among others shapeless, shaped
// and furnace recipes. The client will only be able to craft these recipes.
Recipes []proto.Recipe
// PotionRecipes is a list of all potion mixing recipes which may be used in the brewing stand.
PotionRecipes []protocol.PotionRecipe
// PotionContainerChangeRecipes is a list of all recipes to convert a potion from one type to another,
// such as from a drinkable potion to a splash potion, or from a splash potion to a lingering potion.
PotionContainerChangeRecipes []protocol.PotionContainerChangeRecipe
// MaterialReducers is a list of all material reducers which is used in education edition chemistry.
MaterialReducers []protocol.MaterialReducer
// ClearRecipes indicates if all recipes currently active on the client should be cleaned. Doing this
// means that the client will have no recipes active by itself: Any CraftingData packets previously sent
// will also be discarded, and only the recipes in this CraftingData packet will be used.
ClearRecipes bool
}
// ID ...
func (*CraftingData) ID() uint32 {
return packet.IDCraftingData
}
func (pk *CraftingData) Marshal(io protocol.IO) {
protocol.FuncSlice(io, &pk.Recipes, func(p *proto.Recipe) {
proto.IORecipe(io, p)
})
protocol.Slice(io, &pk.PotionRecipes)
protocol.Slice(io, &pk.PotionContainerChangeRecipes)
protocol.FuncSlice(io, &pk.MaterialReducers, io.MaterialReducer)
io.Bool(&pk.ClearRecipes)
}

View File

@@ -0,0 +1,329 @@
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/nbt"
"github.com/sandertv/gophertunnel/minecraft/protocol"
"github.com/sandertv/gophertunnel/minecraft/protocol/packet"
)
// StartGame is sent by the server to send information about the world the player will be spawned in. It
// contains information about the position the player spawns in, and information about the world in general
// such as its game rules.
type StartGame 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
// 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
// PlayerGameMode is the game mode the player currently has. It is a value from 0-4, with 0 being
// survival mode, 1 being creative mode, 2 being adventure mode, 3 being survival spectator and 4 being
// creative spectator.
// This field may be set to 5 to make the client fall back to the game mode set in the WorldGameMode
// field.
PlayerGameMode int32
// PlayerPosition is the spawn position of the player in the world. In servers this is often the same as
// the world's spawn position found below.
PlayerPosition 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
// WorldSeed is the seed used to generate the world. Unlike in PC edition, the seed is a 32bit integer
// here.
WorldSeed int64
// SpawnBiomeType specifies if the biome that the player spawns in is user defined (through behaviour
// packs) or builtin. See the constants above.
SpawnBiomeType int16
// UserDefinedBiomeName is a readable name of the biome that the player spawned in, such as 'plains'. This
// might be a custom biome name if any custom biomes are present through behaviour packs.
UserDefinedBiomeName string
// Dimension is the ID of the dimension that the player spawns in. It is a value from 0-2, with 0 being
// the overworld, 1 being the nether and 2 being the end.
Dimension int32
// Generator is the generator used for the world. It is a value from 0-4, with 0 being old limited worlds,
// 1 being infinite worlds, 2 being flat worlds, 3 being nether worlds and 4 being end worlds. A value of
// 0 will actually make the client stop rendering chunks you send beyond the world limit.
Generator int32
// 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
// Hardcore is if the world is in hardcore mode. In hardcore mode, the player cannot respawn after dying.
Hardcore bool
// Difficulty is the difficulty of the world. It is a value from 0-3, with 0 being peaceful, 1 being easy,
// 2 being normal and 3 being hard.
Difficulty int32
// 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
// AchievementsDisabled defines if achievements are disabled in the world. The client crashes if this
// 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
// EditorWorldType is a value to dictate the type of editor mode, a special mode recently introduced adding
// "powerful tools for editing worlds, intended for experienced creators." It is one of the constants above.
EditorWorldType int32
// CreatedInEditor is a value to dictate if the world was created as a project in the editor mode. The functionality
// of this field is currently unknown.
CreatedInEditor bool
// ExportedFromEditor is a value to dictate if the world was exported from editor mode. The functionality of this
// field is currently unknown.
ExportedFromEditor 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
// EducationEditionOffer is some Minecraft: Education Edition field that specifies what 'region' the world
// was from, with 0 being None, 1 being RestOfWorld, and 2 being China.
// The actual use of this field is unknown.
EducationEditionOffer int32
// EducationFeaturesEnabled specifies if the world has education edition features enabled, such as the
// blocks or entities specific to education edition.
EducationFeaturesEnabled bool
// EducationProductID is a UUID used to identify the education edition server instance. It is generally
// unique for education edition servers.
EducationProductID string
// RainLevel is the level specifying the intensity of the rain falling. When set to 0, no rain falls at
// all.
RainLevel float32
// LightningLevel is the level specifying the intensity of the thunder. This may actually be set
// independently from the RainLevel, meaning dark clouds can be produced without rain.
LightningLevel float32
// ConfirmedPlatformLockedContent ...
ConfirmedPlatformLockedContent bool
// MultiPlayerGame specifies if the world is a multi-player game. This should always be set to true for
// servers.
MultiPlayerGame bool
// LANBroadcastEnabled specifies if LAN broadcast was intended to be enabled for the world.
LANBroadcastEnabled bool
// XBLBroadcastMode is the mode used to broadcast the joined game across XBOX Live.
XBLBroadcastMode int32
// PlatformBroadcastMode is the mode used to broadcast the joined game across the platform.
PlatformBroadcastMode int32
// CommandsEnabled specifies if commands are enabled for the player. It is recommended to always set this
// to true on the server, as setting it to false means the player cannot, under any circumstance, use a
// command.
CommandsEnabled bool
// TexturePackRequired specifies if the texture pack the world might hold is required, meaning the client
// was forced to download it before joining.
TexturePackRequired bool
// GameRules defines game rules currently active with their respective values. The value of these game
// rules may be either 'bool', 'int32' or 'float32'. Some game rules are server side only, and don't
// necessarily need to be sent to the client.
GameRules []protocol.GameRule
// Experiments holds a list of experiments that are either enabled or disabled in the world that the
// player spawns in.
Experiments []protocol.ExperimentData
// ExperimentsPreviouslyToggled specifies if any experiments were previously toggled in this world. It is
// probably used for some kind of metrics.
ExperimentsPreviouslyToggled bool
// BonusChestEnabled specifies if the world had the bonus map setting enabled when generating it. It does
// not have any effect client-side.
BonusChestEnabled bool
// StartWithMapEnabled specifies if the world has the start with map setting enabled, meaning each joining
// player obtains a map. This should always be set to false, because the client obtains a map all on its
// own accord if this is set to true.
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
// 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
// HasLockedBehaviourPack specifies if the behaviour pack of the world is locked, meaning it cannot be
// disabled from the world. This is typically set for worlds on the marketplace that have a dedicated
// behaviour pack.
HasLockedBehaviourPack bool
// HasLockedTexturePack specifies if the texture pack of the world is locked, meaning it cannot be
// disabled from the world. This is typically set for worlds on the marketplace that have a dedicated
// texture pack.
HasLockedTexturePack bool
// FromLockedWorldTemplate specifies if the world from the server was from a locked world template. For
// servers this should always be set to false.
FromLockedWorldTemplate bool
// MSAGamerTagsOnly ..
MSAGamerTagsOnly bool
// FromWorldTemplate specifies if the world from the server was from a world template. For servers this
// should always be set to false.
FromWorldTemplate bool
// WorldTemplateSettingsLocked specifies if the world was a template that locks all settings that change
// properties above in the settings GUI. It is recommended to set this to true for servers that do not
// allow things such as setting game rules through the GUI.
WorldTemplateSettingsLocked bool
// OnlySpawnV1Villagers is a hack that Mojang put in place to preserve backwards compatibility with old
// villagers. The bool is never actually read though, so it has no functionality.
OnlySpawnV1Villagers bool
// PersonaDisabled is true if persona skins are disabled for the current game session.
PersonaDisabled bool
// CustomSkinsDisabled is true if custom skins are disabled for the current game session.
CustomSkinsDisabled bool
// EmoteChatMuted specifies if players will be sent a chat message when using certain emotes.
EmoteChatMuted bool
// BaseGameVersion is the version of the game from which Vanilla features will be used. The exact function
// of this field isn't clear.
BaseGameVersion string
// LimitedWorldWidth and LimitedWorldDepth are the dimensions of the world if the world is a limited
// world. For unlimited worlds, these may simply be left as 0.
LimitedWorldWidth, LimitedWorldDepth int32
// NewNether specifies if the server runs with the new nether introduced in the 1.16 update.
NewNether bool
// EducationSharedResourceURI is an education edition feature that transmits education resource settings to clients.
EducationSharedResourceURI protocol.EducationSharedResourceURI
// ForceExperimentalGameplay specifies if experimental gameplay should be force enabled. For servers this
// should always be set to false.
ForceExperimentalGameplay protocol.Optional[bool]
// LevelID is a base64 encoded world ID that is used to identify the world.
LevelID string
// WorldName is the name of the world that the player is joining. Note that this field shows up above the
// player list for the rest of the game session, and cannot be changed. Setting the server name to this
// field is recommended.
WorldName string
// TemplateContentIdentity is a UUID specific to the premium world template that might have been used to
// generate the world. Servers should always fill out an empty string for this.
TemplateContentIdentity string
// Trial specifies if the world was a trial world, meaning features are limited and there is a time limit
// on the world.
Trial bool
// PlayerMovementSettings ...
PlayerMovementSettings protocol.PlayerMovementSettings
// Time is the total time that has elapsed since the start of the world.
Time int64
// EnchantmentSeed is the seed used to seed the random used to produce enchantments in the enchantment
// table. Note that the exact correct random implementation must be used to produce the correct results
// both client- and server-side.
EnchantmentSeed int32
// Blocks is a list of all custom blocks registered on the server.
Blocks []protocol.BlockEntry
// Items is a list of all items with their legacy IDs which are available in the game. Failing to send any
// of the items that are in the game will crash mobile clients.
Items []protocol.ItemEntry
// MultiPlayerCorrelationID is a unique ID specifying the multi-player session of the player. A random
// UUID should be filled out for this field.
MultiPlayerCorrelationID string
// ServerAuthoritativeInventory specifies if the server authoritative inventory system is enabled. This
// is a new system introduced in 1.16. Backwards compatibility with the inventory transactions has to
// some extent been preserved, but will eventually be removed.
ServerAuthoritativeInventory bool
// GameVersion is the version of the game the server is running. The exact function of this field isn't clear.
GameVersion string
// PropertyData contains properties that should be applied on the player. These properties are the same as the
// ones that are sent in the SyncActorProperty packet.
PropertyData map[string]any
// ServerBlockStateChecksum is a checksum to ensure block states between the server and client match.
// This can simply be left empty, and the client will avoid trying to verify it.
ServerBlockStateChecksum uint64
// ClientSideGeneration is true if the client should use the features registered in the FeatureRegistry packet to
// generate terrain client-side to save on bandwidth.
ClientSideGeneration bool
// WorldTemplateID is a UUID that identifies the template that was used to generate the world. Servers that do not
// use a world based off of a template can set this to an empty UUID.
WorldTemplateID uuid.UUID
// ChatRestrictionLevel specifies the level of restriction on in-game chat. It is one of the constants above.
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.
UseBlockNetworkIDHashes bool
// ServerAuthoritativeSound is currently unknown as to what it does.
ServerAuthoritativeSound bool
}
// ID ...
func (*StartGame) ID() uint32 {
return packet.IDStartGame
}
func (pk *StartGame) Marshal(io protocol.IO) {
io.Varint64(&pk.EntityUniqueID)
io.Varuint64(&pk.EntityRuntimeID)
io.Varint32(&pk.PlayerGameMode)
io.Vec3(&pk.PlayerPosition)
io.Float32(&pk.Pitch)
io.Float32(&pk.Yaw)
io.Int64(&pk.WorldSeed)
io.Int16(&pk.SpawnBiomeType)
io.String(&pk.UserDefinedBiomeName)
io.Varint32(&pk.Dimension)
io.Varint32(&pk.Generator)
io.Varint32(&pk.WorldGameMode)
io.Bool(&pk.Hardcore)
io.Varint32(&pk.Difficulty)
io.UBlockPos(&pk.WorldSpawn)
io.Bool(&pk.AchievementsDisabled)
io.Varint32(&pk.EditorWorldType)
io.Bool(&pk.CreatedInEditor)
io.Bool(&pk.ExportedFromEditor)
io.Varint32(&pk.DayCycleLockTime)
io.Varint32(&pk.EducationEditionOffer)
io.Bool(&pk.EducationFeaturesEnabled)
io.String(&pk.EducationProductID)
io.Float32(&pk.RainLevel)
io.Float32(&pk.LightningLevel)
io.Bool(&pk.ConfirmedPlatformLockedContent)
io.Bool(&pk.MultiPlayerGame)
io.Bool(&pk.LANBroadcastEnabled)
io.Varint32(&pk.XBLBroadcastMode)
io.Varint32(&pk.PlatformBroadcastMode)
io.Bool(&pk.CommandsEnabled)
io.Bool(&pk.TexturePackRequired)
protocol.FuncSlice(io, &pk.GameRules, io.GameRule)
protocol.SliceUint32Length(io, &pk.Experiments)
io.Bool(&pk.ExperimentsPreviouslyToggled)
io.Bool(&pk.BonusChestEnabled)
io.Bool(&pk.StartWithMapEnabled)
io.Varint32(&pk.PlayerPermissions)
io.Int32(&pk.ServerChunkTickRadius)
io.Bool(&pk.HasLockedBehaviourPack)
io.Bool(&pk.HasLockedTexturePack)
io.Bool(&pk.FromLockedWorldTemplate)
io.Bool(&pk.MSAGamerTagsOnly)
io.Bool(&pk.FromWorldTemplate)
io.Bool(&pk.WorldTemplateSettingsLocked)
io.Bool(&pk.OnlySpawnV1Villagers)
io.Bool(&pk.PersonaDisabled)
io.Bool(&pk.CustomSkinsDisabled)
io.Bool(&pk.EmoteChatMuted)
io.String(&pk.BaseGameVersion)
io.Int32(&pk.LimitedWorldWidth)
io.Int32(&pk.LimitedWorldDepth)
io.Bool(&pk.NewNether)
protocol.Single(io, &pk.EducationSharedResourceURI)
protocol.OptionalFunc(io, &pk.ForceExperimentalGameplay, io.Bool)
io.Uint8(&pk.ChatRestrictionLevel)
io.Bool(&pk.DisablePlayerInteractions)
if proto.IsProtoGTE(io, proto.ID685) {
io.String(&pk.ServerID)
io.String(&pk.WorldID)
io.String(&pk.ScenarioID)
}
io.String(&pk.LevelID)
io.String(&pk.WorldName)
io.String(&pk.TemplateContentIdentity)
io.Bool(&pk.Trial)
protocol.PlayerMoveSettings(io, &pk.PlayerMovementSettings)
io.Int64(&pk.Time)
io.Varint32(&pk.EnchantmentSeed)
protocol.Slice(io, &pk.Blocks)
protocol.Slice(io, &pk.Items)
io.String(&pk.MultiPlayerCorrelationID)
io.Bool(&pk.ServerAuthoritativeInventory)
io.String(&pk.GameVersion)
io.NBT(&pk.PropertyData, nbt.NetworkLittleEndian)
io.Uint64(&pk.ServerBlockStateChecksum)
io.UUID(&pk.WorldTemplateID)
io.Bool(&pk.ClientSideGeneration)
io.Bool(&pk.UseBlockNetworkIDHashes)
io.Bool(&pk.ServerAuthoritativeSound)
}

View File

@@ -0,0 +1,79 @@
package legacypacket
import (
"github.com/akmalfairuz/legacy-version/legacyver/proto"
"github.com/sandertv/gophertunnel/minecraft/protocol"
"github.com/sandertv/gophertunnel/minecraft/protocol/packet"
)
const (
TextTypeRaw = iota
TextTypeChat
TextTypeTranslation
TextTypePopup
TextTypeJukeboxPopup
TextTypeTip
TextTypeSystem
TextTypeWhisper
TextTypeAnnouncement
TextTypeObjectWhisper
TextTypeObject
TextTypeObjectAnnouncement
)
// Text is sent by the client to the server to send chat messages, and by the server to the client to forward
// or send messages, which may be chat, popups, tips etc.
type Text struct {
// TextType is the type of the text sent. When a client sends this to the server, it should always be
// TextTypeChat. If the server sends it, it may be one of the other text types above.
TextType byte
// NeedsTranslation specifies if any of the messages need to be translated. It seems that where % is found
// in translatable text types, these are translated regardless of this bool. Translatable text types
// include TextTypeTranslation, TextTypeTip, TextTypePopup and TextTypeJukeboxPopup.
NeedsTranslation bool
// SourceName is the name of the source of the messages. This source is displayed in text types such as
// the TextTypeChat and TextTypeWhisper, where typically the username is shown.
SourceName string
// Message is the message of the packet. This field is set for each TextType and is the main component of
// the packet.
Message string
// Parameters is a list of parameters that should be filled into the message. These parameters are only
// written if the type of the packet is TextTypeTranslation, TextTypeTip, TextTypePopup or TextTypeJukeboxPopup.
Parameters []string
// XUID is the XBOX Live user ID of the player that sent the message. It is only set for packets of
// TextTypeChat. When sent to a player, the player will only be shown the chat message if a player with
// this XUID is present in the player list and not muted, or if the XUID is empty.
XUID string
// 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
// 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 (*Text) ID() uint32 {
return packet.IDText
}
func (pk *Text) Marshal(io protocol.IO) {
io.Uint8(&pk.TextType)
io.Bool(&pk.NeedsTranslation)
switch pk.TextType {
case TextTypeChat, TextTypeWhisper, TextTypeAnnouncement:
io.String(&pk.SourceName)
io.String(&pk.Message)
case TextTypeRaw, TextTypeTip, TextTypeSystem, TextTypeObject, TextTypeObjectWhisper, TextTypeObjectAnnouncement:
io.String(&pk.Message)
case TextTypeTranslation, TextTypePopup, TextTypeJukeboxPopup:
io.String(&pk.Message)
protocol.FuncSlice(io, &pk.Parameters, io.String)
}
io.String(&pk.XUID)
io.String(&pk.PlatformChatID)
if proto.IsProtoGTE(io, proto.ID685) {
io.String(&pk.FilteredMessage)
}
}

View File

@@ -9,6 +9,7 @@ const (
ID712 = 712 // v1.21.20 ID712 = 712 // v1.21.20
ID686 = 686 // v1.21.2 ID686 = 686 // v1.21.2
ID685 = 685 // v1.21.0 ID685 = 685 // v1.21.0
ID671 = 671 // v1.20.80
) )
func IsProtoGTE(io protocol.IO, proto int32) bool { func IsProtoGTE(io protocol.IO, proto int32) bool {

View File

@@ -112,3 +112,22 @@ func IOStackRequestAction(io protocol.IO, x *protocol.StackRequestAction) {
} }
(*x).Marshal(io) (*x).Marshal(io)
} }
func IORecipe(io protocol.IO, recipe *Recipe) {
if IsReader(io) {
var recipeType int32
io.Varint32(&recipeType)
if !lookupRecipe(recipeType, recipe) {
io.UnknownEnumOption(recipeType, "crafting data recipe type")
return
}
(*recipe).Unmarshal(io.(*Reader))
} else {
var recipeType int32
if !lookupRecipeType(*recipe, &recipeType) {
io.UnknownEnumOption(fmt.Sprintf("%T", *recipe), "crafting recipe type")
}
io.Varint32(&recipeType)
(*recipe).Marshal(io.(*Writer))
}
}

View File

@@ -44,6 +44,27 @@ func (x *ItemStackRequest) FromLatest(y protocol.ItemStackRequest) ItemStackRequ
for i, v := range y.Actions { for i, v := range y.Actions {
if z, ok := v.(*protocol.CraftRecipeStackRequestAction); ok { if z, ok := v.(*protocol.CraftRecipeStackRequestAction); ok {
x.Actions[i] = (&CraftRecipeStackRequestAction{}).FromLatest(z) x.Actions[i] = (&CraftRecipeStackRequestAction{}).FromLatest(z)
continue
}
if z, ok := v.(*protocol.AutoCraftRecipeStackRequestAction); ok {
x.Actions[i] = (&AutoCraftRecipeStackRequestAction{}).FromLatest(z)
continue
}
if z, ok := v.(*protocol.CraftCreativeStackRequestAction); ok {
x.Actions[i] = (&CraftCreativeStackRequestAction{}).FromLatest(z)
continue
}
if z, ok := v.(*protocol.CraftGrindstoneRecipeStackRequestAction); ok {
x.Actions[i] = (&CraftGrindstoneRecipeStackRequestAction{}).FromLatest(z)
continue
}
if z, ok := v.(*protocol.CraftLoomRecipeStackRequestAction); ok {
x.Actions[i] = (&CraftLoomRecipeStackRequestAction{}).FromLatest(z)
continue
} }
} }
x.FilterStrings = y.FilterStrings x.FilterStrings = y.FilterStrings
@@ -61,6 +82,27 @@ func (x *ItemStackRequest) ToLatest() protocol.ItemStackRequest {
for i, v := range x.Actions { for i, v := range x.Actions {
if z, ok := v.(*CraftRecipeStackRequestAction); ok { if z, ok := v.(*CraftRecipeStackRequestAction); ok {
ret.Actions[i] = z.ToLatest() ret.Actions[i] = z.ToLatest()
continue
}
if z, ok := v.(*AutoCraftRecipeStackRequestAction); ok {
ret.Actions[i] = z.ToLatest()
continue
}
if z, ok := v.(*CraftCreativeStackRequestAction); ok {
ret.Actions[i] = z.ToLatest()
continue
}
if z, ok := v.(*CraftGrindstoneRecipeStackRequestAction); ok {
ret.Actions[i] = z.ToLatest()
continue
}
if z, ok := v.(*CraftLoomRecipeStackRequestAction); ok {
ret.Actions[i] = z.ToLatest()
continue
} }
} }
return ret return ret
@@ -101,15 +143,15 @@ func lookupStackRequestActionType(x StackRequestAction, id *uint8) bool {
*id = protocol.StackRequestActionMineBlock *id = protocol.StackRequestActionMineBlock
case *CraftRecipeStackRequestAction: case *CraftRecipeStackRequestAction:
*id = protocol.StackRequestActionCraftRecipe *id = protocol.StackRequestActionCraftRecipe
case *protocol.AutoCraftRecipeStackRequestAction: case *AutoCraftRecipeStackRequestAction:
*id = protocol.StackRequestActionCraftRecipeAuto *id = protocol.StackRequestActionCraftRecipeAuto
case *protocol.CraftCreativeStackRequestAction: case *CraftCreativeStackRequestAction:
*id = protocol.StackRequestActionCraftCreative *id = protocol.StackRequestActionCraftCreative
case *protocol.CraftRecipeOptionalStackRequestAction: case *protocol.CraftRecipeOptionalStackRequestAction:
*id = protocol.StackRequestActionCraftRecipeOptional *id = protocol.StackRequestActionCraftRecipeOptional
case *protocol.CraftGrindstoneRecipeStackRequestAction: case *CraftGrindstoneRecipeStackRequestAction:
*id = protocol.StackRequestActionCraftGrindstone *id = protocol.StackRequestActionCraftGrindstone
case *protocol.CraftLoomRecipeStackRequestAction: case *CraftLoomRecipeStackRequestAction:
*id = protocol.StackRequestActionCraftLoom *id = protocol.StackRequestActionCraftLoom
case *protocol.CraftNonImplementedStackRequestAction: case *protocol.CraftNonImplementedStackRequestAction:
*id = protocol.StackRequestActionCraftNonImplementedDeprecated *id = protocol.StackRequestActionCraftNonImplementedDeprecated
@@ -151,15 +193,15 @@ func lookupStackRequestAction(id uint8, x *protocol.StackRequestAction) bool {
case protocol.StackRequestActionCraftRecipe: case protocol.StackRequestActionCraftRecipe:
*x = &CraftRecipeStackRequestAction{} *x = &CraftRecipeStackRequestAction{}
case protocol.StackRequestActionCraftRecipeAuto: case protocol.StackRequestActionCraftRecipeAuto:
*x = &protocol.AutoCraftRecipeStackRequestAction{} *x = &AutoCraftRecipeStackRequestAction{}
case protocol.StackRequestActionCraftCreative: case protocol.StackRequestActionCraftCreative:
*x = &protocol.CraftCreativeStackRequestAction{} *x = &CraftCreativeStackRequestAction{}
case protocol.StackRequestActionCraftRecipeOptional: case protocol.StackRequestActionCraftRecipeOptional:
*x = &protocol.CraftRecipeOptionalStackRequestAction{} *x = &protocol.CraftRecipeOptionalStackRequestAction{}
case protocol.StackRequestActionCraftGrindstone: case protocol.StackRequestActionCraftGrindstone:
*x = &protocol.CraftGrindstoneRecipeStackRequestAction{} *x = &CraftGrindstoneRecipeStackRequestAction{}
case protocol.StackRequestActionCraftLoom: case protocol.StackRequestActionCraftLoom:
*x = &protocol.CraftLoomRecipeStackRequestAction{} *x = &CraftLoomRecipeStackRequestAction{}
case protocol.StackRequestActionCraftNonImplementedDeprecated: case protocol.StackRequestActionCraftNonImplementedDeprecated:
*x = &protocol.CraftNonImplementedStackRequestAction{} *x = &protocol.CraftNonImplementedStackRequestAction{}
case protocol.StackRequestActionCraftResultsDeprecated: case protocol.StackRequestActionCraftResultsDeprecated:
@@ -602,11 +644,32 @@ type AutoCraftRecipeStackRequestAction struct {
Ingredients []protocol.ItemDescriptorCount Ingredients []protocol.ItemDescriptorCount
} }
// FromLatest ...
func (a *AutoCraftRecipeStackRequestAction) FromLatest(y *protocol.AutoCraftRecipeStackRequestAction) *AutoCraftRecipeStackRequestAction {
a.RecipeNetworkID = y.RecipeNetworkID
a.NumberOfCrafts = y.NumberOfCrafts
a.TimesCrafted = y.TimesCrafted
a.Ingredients = y.Ingredients
return a
}
// ToLatest ...
func (a *AutoCraftRecipeStackRequestAction) ToLatest() *protocol.AutoCraftRecipeStackRequestAction {
return &protocol.AutoCraftRecipeStackRequestAction{
RecipeNetworkID: a.RecipeNetworkID,
NumberOfCrafts: a.NumberOfCrafts,
TimesCrafted: a.TimesCrafted,
Ingredients: a.Ingredients,
}
}
// Marshal ... // Marshal ...
func (a *AutoCraftRecipeStackRequestAction) Marshal(r protocol.IO) { func (a *AutoCraftRecipeStackRequestAction) Marshal(r protocol.IO) {
r.Varuint32(&a.RecipeNetworkID) r.Varuint32(&a.RecipeNetworkID)
r.Uint8(&a.NumberOfCrafts) r.Uint8(&a.NumberOfCrafts)
if IsProtoGTE(r, ID712) {
r.Uint8(&a.TimesCrafted) r.Uint8(&a.TimesCrafted)
}
protocol.FuncSlice(r, &a.Ingredients, r.ItemDescriptorCount) protocol.FuncSlice(r, &a.Ingredients, r.ItemDescriptorCount)
} }
@@ -621,11 +684,28 @@ type CraftCreativeStackRequestAction struct {
NumberOfCrafts byte NumberOfCrafts byte
} }
// FromLatest ...
func (a *CraftCreativeStackRequestAction) FromLatest(y *protocol.CraftCreativeStackRequestAction) *CraftCreativeStackRequestAction {
a.CreativeItemNetworkID = y.CreativeItemNetworkID
a.NumberOfCrafts = y.NumberOfCrafts
return a
}
// ToLatest ...
func (a *CraftCreativeStackRequestAction) ToLatest() *protocol.CraftCreativeStackRequestAction {
return &protocol.CraftCreativeStackRequestAction{
CreativeItemNetworkID: a.CreativeItemNetworkID,
NumberOfCrafts: a.NumberOfCrafts,
}
}
// Marshal ... // Marshal ...
func (a *CraftCreativeStackRequestAction) Marshal(r protocol.IO) { func (a *CraftCreativeStackRequestAction) Marshal(r protocol.IO) {
r.Varuint32(&a.CreativeItemNetworkID) r.Varuint32(&a.CreativeItemNetworkID)
if IsProtoGTE(r, ID712) {
r.Uint8(&a.NumberOfCrafts) r.Uint8(&a.NumberOfCrafts)
} }
}
// CraftRecipeOptionalStackRequestAction is sent when using an anvil. When this action is sent, the // CraftRecipeOptionalStackRequestAction is sent when using an anvil. When this action is sent, the
// FilterStrings field in the respective stack request is non-empty and contains the name of the item created // FilterStrings field in the respective stack request is non-empty and contains the name of the item created
@@ -659,10 +739,29 @@ type CraftGrindstoneRecipeStackRequestAction struct {
Cost int32 Cost int32
} }
// FromLatest ...
func (c *CraftGrindstoneRecipeStackRequestAction) FromLatest(y *protocol.CraftGrindstoneRecipeStackRequestAction) *CraftGrindstoneRecipeStackRequestAction {
c.RecipeNetworkID = y.RecipeNetworkID
c.NumberOfCrafts = y.NumberOfCrafts
c.Cost = y.Cost
return c
}
// ToLatest ...
func (c *CraftGrindstoneRecipeStackRequestAction) ToLatest() *protocol.CraftGrindstoneRecipeStackRequestAction {
return &protocol.CraftGrindstoneRecipeStackRequestAction{
RecipeNetworkID: c.RecipeNetworkID,
NumberOfCrafts: c.NumberOfCrafts,
Cost: c.Cost,
}
}
// Marshal ... // Marshal ...
func (c *CraftGrindstoneRecipeStackRequestAction) Marshal(r protocol.IO) { func (c *CraftGrindstoneRecipeStackRequestAction) Marshal(r protocol.IO) {
r.Varuint32(&c.RecipeNetworkID) r.Varuint32(&c.RecipeNetworkID)
if IsProtoGTE(r, ID712) {
r.Uint8(&c.NumberOfCrafts) r.Uint8(&c.NumberOfCrafts)
}
r.Varint32(&c.Cost) r.Varint32(&c.Cost)
} }
@@ -675,11 +774,28 @@ type CraftLoomRecipeStackRequestAction struct {
TimesCrafted byte TimesCrafted byte
} }
// FromLatest ...
func (c *CraftLoomRecipeStackRequestAction) FromLatest(y *protocol.CraftLoomRecipeStackRequestAction) *CraftLoomRecipeStackRequestAction {
c.Pattern = y.Pattern
c.TimesCrafted = y.TimesCrafted
return c
}
// ToLatest ...
func (c *CraftLoomRecipeStackRequestAction) ToLatest() *protocol.CraftLoomRecipeStackRequestAction {
return &protocol.CraftLoomRecipeStackRequestAction{
Pattern: c.Pattern,
TimesCrafted: c.TimesCrafted,
}
}
// Marshal ... // Marshal ...
func (c *CraftLoomRecipeStackRequestAction) Marshal(r protocol.IO) { func (c *CraftLoomRecipeStackRequestAction) Marshal(r protocol.IO) {
r.String(&c.Pattern) r.String(&c.Pattern)
if IsProtoGTE(r, ID712) {
r.Uint8(&c.TimesCrafted) r.Uint8(&c.TimesCrafted)
} }
}
// CraftNonImplementedStackRequestAction is an action sent for inventory actions that aren't yet implemented // CraftNonImplementedStackRequestAction is an action sent for inventory actions that aren't yet implemented
// in the new system. These include, for example, anvils. // in the new system. These include, for example, anvils.

699
legacyver/proto/recipe.go Normal file
View File

@@ -0,0 +1,699 @@
package proto
import (
"github.com/google/uuid"
"github.com/sandertv/gophertunnel/minecraft/protocol"
)
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 []protocol.ItemDescriptorCount
}
func (x *RecipeUnlockRequirement) FromLatest(requirement protocol.RecipeUnlockRequirement) RecipeUnlockRequirement {
x.Context = requirement.Context
x.Ingredients = requirement.Ingredients
return *x
}
func (x *RecipeUnlockRequirement) ToLatest() protocol.RecipeUnlockRequirement {
return protocol.RecipeUnlockRequirement{
Context: x.Context,
Ingredients: x.Ingredients,
}
}
// Marshal ...
func (x *RecipeUnlockRequirement) Marshal(r protocol.IO) {
r.Uint8(&x.Context)
if x.Context == RecipeUnlockContextNone {
protocol.FuncSlice(r, &x.Ingredients, r.ItemDescriptorCount)
}
}
const (
RecipeShapeless int32 = iota
RecipeShaped
RecipeFurnace
RecipeFurnaceData
RecipeMulti
RecipeShulkerBox
RecipeShapelessChemistry
RecipeShapedChemistry
RecipeSmithingTransform
RecipeSmithingTrim
)
// Recipe represents a recipe that may be sent in a CraftingData packet to let the client know what recipes
// are available server-side.
type Recipe interface {
// Marshal encodes the recipe data to its binary representation into buf.
Marshal(w *Writer)
// Unmarshal decodes a serialised recipe from Reader r into the recipe instance.
Unmarshal(r *Reader)
}
func takePtr[T any](x T) *T {
return &x
}
func RecipeToLatest(x Recipe) protocol.Recipe {
switch x := x.(type) {
case *ShapelessRecipe:
return takePtr(x.ToLatest())
case *ShulkerBoxRecipe:
return takePtr(x.ToLatest())
case *ShapelessChemistryRecipe:
return takePtr(x.ToLatest())
case *ShapedRecipe:
return takePtr(x.ToLatest())
case *ShapedChemistryRecipe:
return takePtr(x.ToLatest())
case *FurnaceRecipe:
return takePtr(x.ToLatest())
case *FurnaceDataRecipe:
return takePtr(x.ToLatest())
case *MultiRecipe:
return takePtr(x.ToLatest())
case *SmithingTransformRecipe:
return takePtr(x.ToLatest())
case *SmithingTrimRecipe:
return takePtr(x.ToLatest())
}
panic("RecipeToLatest: unknown recipe type")
}
func RecipeFromLatest(x protocol.Recipe) Recipe {
switch x := x.(type) {
case *protocol.ShapelessRecipe:
return (&ShapelessRecipe{}).FromLatest(*x)
case *protocol.ShulkerBoxRecipe:
return (&ShulkerBoxRecipe{}).FromLatest(*x)
case *protocol.ShapelessChemistryRecipe:
return (&ShapelessChemistryRecipe{}).FromLatest(*x)
case *protocol.ShapedRecipe:
return (&ShapedRecipe{}).FromLatest(*x)
case *protocol.ShapedChemistryRecipe:
return (&ShapedChemistryRecipe{}).FromLatest(*x)
case *protocol.FurnaceRecipe:
return (&FurnaceRecipe{}).FromLatest(*x)
case *protocol.FurnaceDataRecipe:
return (&FurnaceDataRecipe{}).FromLatest(*x)
case *protocol.MultiRecipe:
return (&MultiRecipe{}).FromLatest(*x)
case *protocol.SmithingTransformRecipe:
return (&SmithingTransformRecipe{}).FromLatest(*x)
case *protocol.SmithingTrimRecipe:
return (&SmithingTrimRecipe{}).FromLatest(*x)
}
panic("RecipeFromLatest: unknown recipe type")
}
// lookupRecipe looks up the Recipe for a recipe type. False is returned if not
// found.
func lookupRecipe(recipeType int32, x *Recipe) bool {
switch recipeType {
case RecipeShapeless:
*x = &ShapelessRecipe{}
case RecipeShaped:
*x = &ShapedRecipe{}
case RecipeFurnace:
*x = &FurnaceRecipe{}
case RecipeFurnaceData:
*x = &FurnaceDataRecipe{}
case RecipeMulti:
*x = &MultiRecipe{}
case RecipeShulkerBox:
*x = &ShulkerBoxRecipe{}
case RecipeShapelessChemistry:
*x = &ShapelessChemistryRecipe{}
case RecipeShapedChemistry:
*x = &ShapedChemistryRecipe{}
case RecipeSmithingTransform:
*x = &SmithingTransformRecipe{}
case RecipeSmithingTrim:
*x = &SmithingTrimRecipe{}
default:
return false
}
return true
}
// lookupRecipeType looks up the recipe type for a Recipe. False is returned if
// none was found.
func lookupRecipeType(x Recipe, recipeType *int32) bool {
switch x.(type) {
case *ShapelessRecipe:
*recipeType = RecipeShapeless
case *ShapedRecipe:
*recipeType = RecipeShaped
case *FurnaceRecipe:
*recipeType = RecipeFurnace
case *FurnaceDataRecipe:
*recipeType = RecipeFurnaceData
case *MultiRecipe:
*recipeType = RecipeMulti
case *ShulkerBoxRecipe:
*recipeType = RecipeShulkerBox
case *ShapelessChemistryRecipe:
*recipeType = RecipeShapelessChemistry
case *ShapedChemistryRecipe:
*recipeType = RecipeShapedChemistry
case *SmithingTransformRecipe:
*recipeType = RecipeSmithingTransform
case *SmithingTrimRecipe:
*recipeType = RecipeSmithingTrim
default:
return false
}
return true
}
// ShapelessRecipe is a recipe that has no particular shape. Its functionality is shared with the
// RecipeShulkerBox and RecipeShapelessChemistry types.
type ShapelessRecipe struct {
// RecipeID is a unique ID of the recipe. This ID must be unique amongst all other types of recipes too,
// but its functionality is not exactly known.
RecipeID string
// Input is a list of items that serve as the input of the shapeless recipe. These items are the items
// required to craft the output.
Input []protocol.ItemDescriptorCount
// Output is a list of items that are created as a result of crafting the recipe.
Output []protocol.ItemStack
// UUID is a UUID identifying the recipe. Since the CraftingEvent packet no longer exists, this can always be empty.
UUID uuid.UUID
// Block is the block name that is required to craft the output of the recipe. The block is not prefixed
// with 'minecraft:', so it will look like 'crafting_table' as an example.
// The available blocks are:
// - crafting_table
// - cartography_table
// - stonecutter
// - furnace
// - blast_furnace
// - smoker
// - campfire
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.
RecipeNetworkID uint32
}
// ShulkerBoxRecipe is a shapeless recipe made specifically for shulker box crafting, so that they don't lose
// their user data when dyeing a shulker box.
type ShulkerBoxRecipe struct {
ShapelessRecipe
}
// ShapelessChemistryRecipe is a recipe specifically made for chemistry related features, which exist only in
// the Education Edition. They function the same as shapeless recipes do.
type ShapelessChemistryRecipe struct {
ShapelessRecipe
}
// ShapedRecipe is a recipe that has a specific shape that must be used to craft the output of the recipe.
// Trying to craft the item in any other shape will not work. The ShapedRecipe is of the same structure as the
// ShapedChemistryRecipe.
type ShapedRecipe struct {
// RecipeID is a unique ID of the recipe. This ID must be unique amongst all other types of recipes too,
// but its functionality is not exactly known.
RecipeID string
// Width is the width of the recipe's shape.
Width int32
// Height is the height of the recipe's shape.
Height int32
// Input is a list of items that serve as the input of the shapeless recipe. These items are the items
// required to craft the output. The amount of input items must be exactly equal to Width * Height.
Input []protocol.ItemDescriptorCount
// Output is a list of items that are created as a result of crafting the recipe.
Output []protocol.ItemStack
// UUID is a UUID identifying the recipe. Since the CraftingEvent packet no longer exists, this can always be empty.
UUID uuid.UUID
// Block is the block name that is required to craft the output of the recipe. The block is not prefixed
// with 'minecraft:', so it will look like 'crafting_table' as an example.
Block string
// Priority ...
Priority int32
// 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.
RecipeNetworkID uint32
}
// ShapedChemistryRecipe is a recipe specifically made for chemistry related features, which exist only in the
// Education Edition. It functions the same as a normal ShapedRecipe.
type ShapedChemistryRecipe struct {
ShapedRecipe
}
// FurnaceRecipe is a recipe that is specifically used for all kinds of furnaces. These recipes don't just
// apply to furnaces, but also blast furnaces and smokers.
type FurnaceRecipe struct {
// InputType is the item type of the input item. The metadata value of the item is not used in the
// FurnaceRecipe. Use FurnaceDataRecipe to allow an item with only one metadata value.
InputType protocol.ItemType
// Output is the item that is created as a result of smelting/cooking an item in the furnace.
Output protocol.ItemStack
// Block is the block name that is required to create the output of the recipe. The block is not prefixed
// with 'minecraft:', so it will look like 'furnace' as an example.
Block string
}
// FurnaceDataRecipe is a recipe specifically used for furnace-type crafting stations. It is equal to
// FurnaceRecipe, except it has an input item with a specific metadata value, instead of any metadata value.
type FurnaceDataRecipe struct {
FurnaceRecipe
}
// MultiRecipe serves as an 'enable' switch for multi-shape recipes.
type MultiRecipe struct {
// UUID is a UUID identifying the recipe. Since the CraftingEvent packet no longer exists, this can always be empty.
UUID uuid.UUID
// 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.
RecipeNetworkID uint32
}
// SmithingTransformRecipe is a recipe specifically used for smithing tables. It has three input items and adds them
// together, resulting in a new item.
type SmithingTransformRecipe struct {
// 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.
RecipeNetworkID uint32
// RecipeID is a unique ID of the recipe. This ID must be unique amongst all other types of recipes too,
// but its functionality is not exactly known.
RecipeID string
// Template is the item that is used to shape the Base item based on the Addition being applied.
Template protocol.ItemDescriptorCount
// Base is the item that the Addition is being applied to in the smithing table.
Base protocol.ItemDescriptorCount
// Addition is the item that is being added to the Base item to result in a modified item.
Addition protocol.ItemDescriptorCount
// Result is the resulting item from the two items being added together.
Result protocol.ItemStack
// Block is the block name that is required to create the output of the recipe. The block is not prefixed with
// 'minecraft:', so it will look like 'smithing_table' as an example.
Block string
}
// SmithingTrimRecipe is a recipe specifically used for applying armour trims to an armour piece inside a smithing table.
type SmithingTrimRecipe struct {
// 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.
RecipeNetworkID uint32
// RecipeID is a unique ID of the recipe. This ID must be unique amongst all other types of recipes too,
// but its functionality is not exactly known.
RecipeID string
// Template is the item that is used to shape the Base item based on the Addition being applied.
Template protocol.ItemDescriptorCount
// Base is the item that the Addition is being applied to in the smithing table.
Base protocol.ItemDescriptorCount
// Addition is the item that is being added to the Base item to result in a modified item.
Addition protocol.ItemDescriptorCount
// Block is the block name that is required to create the output of the recipe. The block is not prefixed with
// 'minecraft:', so it will look like 'smithing_table' as an example.
Block string
}
// FromLatest ...
func (recipe *ShapelessRecipe) FromLatest(v protocol.ShapelessRecipe) *ShapelessRecipe {
recipe.RecipeID = v.RecipeID
recipe.Input = v.Input
recipe.Output = v.Output
recipe.UUID = v.UUID
recipe.Block = v.Block
recipe.Priority = v.Priority
recipe.UnlockRequirement = (&RecipeUnlockRequirement{}).FromLatest(v.UnlockRequirement)
recipe.RecipeNetworkID = v.RecipeNetworkID
return recipe
}
// ToLatest ...
func (recipe *ShapelessRecipe) ToLatest() protocol.ShapelessRecipe {
return protocol.ShapelessRecipe{
RecipeID: recipe.RecipeID,
Input: recipe.Input,
Output: recipe.Output,
UUID: recipe.UUID,
Block: recipe.Block,
Priority: recipe.Priority,
UnlockRequirement: recipe.UnlockRequirement.ToLatest(),
RecipeNetworkID: recipe.RecipeNetworkID,
}
}
// Marshal ...
func (recipe *ShapelessRecipe) Marshal(w *Writer) {
marshalShapeless(w, recipe)
}
// Unmarshal ...
func (recipe *ShapelessRecipe) Unmarshal(r *Reader) {
marshalShapeless(r, recipe)
}
// FromLatest ...
func (recipe *ShulkerBoxRecipe) FromLatest(v protocol.ShulkerBoxRecipe) *ShulkerBoxRecipe {
recipe.ShapelessRecipe = *(&ShapelessRecipe{}).FromLatest(v.ShapelessRecipe)
return recipe
}
// ToLatest ...
func (recipe *ShulkerBoxRecipe) ToLatest() protocol.ShulkerBoxRecipe {
return protocol.ShulkerBoxRecipe{
ShapelessRecipe: recipe.ShapelessRecipe.ToLatest(),
}
}
// Marshal ...
func (recipe *ShulkerBoxRecipe) Marshal(w *Writer) {
marshalShapeless(w, &recipe.ShapelessRecipe)
}
// Unmarshal ...
func (recipe *ShulkerBoxRecipe) Unmarshal(r *Reader) {
marshalShapeless(r, &recipe.ShapelessRecipe)
}
// FromLatest ...
func (recipe *ShapelessChemistryRecipe) FromLatest(v protocol.ShapelessChemistryRecipe) *ShapelessChemistryRecipe {
recipe.ShapelessRecipe = *(&ShapelessRecipe{}).FromLatest(v.ShapelessRecipe)
return recipe
}
// ToLatest ...
func (recipe *ShapelessChemistryRecipe) ToLatest() protocol.ShapelessChemistryRecipe {
return protocol.ShapelessChemistryRecipe{
ShapelessRecipe: recipe.ShapelessRecipe.ToLatest(),
}
}
// Marshal ...
func (recipe *ShapelessChemistryRecipe) Marshal(w *Writer) {
marshalShapeless(w, &recipe.ShapelessRecipe)
}
// Unmarshal ...
func (recipe *ShapelessChemistryRecipe) Unmarshal(r *Reader) {
marshalShapeless(r, &recipe.ShapelessRecipe)
}
// FromLatest ...
func (recipe *ShapedRecipe) FromLatest(v protocol.ShapedRecipe) *ShapedRecipe {
recipe.RecipeID = v.RecipeID
recipe.Width = v.Width
recipe.Height = v.Height
recipe.Input = v.Input
recipe.Output = v.Output
recipe.UUID = v.UUID
recipe.Block = v.Block
recipe.AssumeSymmetry = v.AssumeSymmetry
recipe.UnlockRequirement = (&RecipeUnlockRequirement{}).FromLatest(v.UnlockRequirement)
recipe.Priority = v.Priority
recipe.RecipeNetworkID = v.RecipeNetworkID
return recipe
}
// ToLatest ...
func (recipe *ShapedRecipe) ToLatest() protocol.ShapedRecipe {
return protocol.ShapedRecipe{
RecipeID: recipe.RecipeID,
Width: recipe.Width,
Height: recipe.Height,
Input: recipe.Input,
Output: recipe.Output,
UUID: recipe.UUID,
Block: recipe.Block,
AssumeSymmetry: recipe.AssumeSymmetry,
UnlockRequirement: recipe.UnlockRequirement.ToLatest(),
Priority: recipe.Priority,
RecipeNetworkID: recipe.RecipeNetworkID,
}
}
// Marshal ...
func (recipe *ShapedRecipe) Marshal(w *Writer) {
marshalShaped(w, recipe)
}
// Unmarshal ...
func (recipe *ShapedRecipe) Unmarshal(r *Reader) {
marshalShaped(r, recipe)
}
// FromLatest ...
func (recipe *ShapedChemistryRecipe) FromLatest(v protocol.ShapedChemistryRecipe) *ShapedChemistryRecipe {
recipe.ShapedRecipe = *(&ShapedRecipe{}).FromLatest(v.ShapedRecipe)
return recipe
}
// ToLatest ...
func (recipe *ShapedChemistryRecipe) ToLatest() protocol.ShapedChemistryRecipe {
return protocol.ShapedChemistryRecipe{
ShapedRecipe: recipe.ShapedRecipe.ToLatest(),
}
}
// Marshal ...
func (recipe *ShapedChemistryRecipe) Marshal(w *Writer) {
marshalShaped(w, &recipe.ShapedRecipe)
}
// Unmarshal ...
func (recipe *ShapedChemistryRecipe) Unmarshal(r *Reader) {
marshalShaped(r, &recipe.ShapedRecipe)
}
// FromLatest ...
func (recipe *FurnaceRecipe) FromLatest(v protocol.FurnaceRecipe) *FurnaceRecipe {
recipe.InputType = v.InputType
recipe.Output = v.Output
recipe.Block = v.Block
return recipe
}
// ToLatest ...
func (recipe *FurnaceRecipe) ToLatest() protocol.FurnaceRecipe {
return protocol.FurnaceRecipe{
InputType: recipe.InputType,
Output: recipe.Output,
Block: recipe.Block,
}
}
// Marshal ...
func (recipe *FurnaceRecipe) Marshal(w *Writer) {
w.Varint32(&recipe.InputType.NetworkID)
w.Item(&recipe.Output)
w.String(&recipe.Block)
}
// Unmarshal ...
func (recipe *FurnaceRecipe) Unmarshal(r *Reader) {
r.Varint32(&recipe.InputType.NetworkID)
r.Item(&recipe.Output)
r.String(&recipe.Block)
}
// FromLatest ...
func (recipe *FurnaceDataRecipe) FromLatest(v protocol.FurnaceDataRecipe) *FurnaceDataRecipe {
recipe.FurnaceRecipe = *(&FurnaceRecipe{}).FromLatest(v.FurnaceRecipe)
return recipe
}
// ToLatest ...
func (recipe *FurnaceDataRecipe) ToLatest() protocol.FurnaceDataRecipe {
return protocol.FurnaceDataRecipe{
FurnaceRecipe: recipe.FurnaceRecipe.ToLatest(),
}
}
// Marshal ...
func (recipe *FurnaceDataRecipe) Marshal(w *Writer) {
w.Varint32(&recipe.InputType.NetworkID)
aux := int32(recipe.InputType.MetadataValue)
w.Varint32(&aux)
w.Item(&recipe.Output)
w.String(&recipe.Block)
}
// Unmarshal ...
func (recipe *FurnaceDataRecipe) Unmarshal(r *Reader) {
var dataValue int32
r.Varint32(&recipe.InputType.NetworkID)
r.Varint32(&dataValue)
recipe.InputType.MetadataValue = uint32(dataValue)
r.Item(&recipe.Output)
r.String(&recipe.Block)
}
// FromLatest ...
func (recipe *MultiRecipe) FromLatest(v protocol.MultiRecipe) *MultiRecipe {
recipe.UUID = v.UUID
recipe.RecipeNetworkID = v.RecipeNetworkID
return recipe
}
// ToLatest ...
func (recipe *MultiRecipe) ToLatest() protocol.MultiRecipe {
return protocol.MultiRecipe{
UUID: recipe.UUID,
RecipeNetworkID: recipe.RecipeNetworkID,
}
}
// Marshal ...
func (recipe *MultiRecipe) Marshal(w *Writer) {
w.UUID(&recipe.UUID)
w.Varuint32(&recipe.RecipeNetworkID)
}
// Unmarshal ...
func (recipe *MultiRecipe) Unmarshal(r *Reader) {
r.UUID(&recipe.UUID)
r.Varuint32(&recipe.RecipeNetworkID)
}
// FromLatest ...
func (recipe *SmithingTransformRecipe) FromLatest(v protocol.SmithingTransformRecipe) *SmithingTransformRecipe {
recipe.RecipeNetworkID = v.RecipeNetworkID
recipe.RecipeID = v.RecipeID
recipe.Template = v.Template
recipe.Base = v.Base
recipe.Addition = v.Addition
recipe.Result = v.Result
recipe.Block = v.Block
return recipe
}
// ToLatest ...
func (recipe *SmithingTransformRecipe) ToLatest() protocol.SmithingTransformRecipe {
return protocol.SmithingTransformRecipe{
RecipeNetworkID: recipe.RecipeNetworkID,
RecipeID: recipe.RecipeID,
Template: recipe.Template,
Base: recipe.Base,
Addition: recipe.Addition,
Result: recipe.Result,
Block: recipe.Block,
}
}
// Marshal ...
func (recipe *SmithingTransformRecipe) Marshal(w *Writer) {
w.String(&recipe.RecipeID)
w.ItemDescriptorCount(&recipe.Template)
w.ItemDescriptorCount(&recipe.Base)
w.ItemDescriptorCount(&recipe.Addition)
w.Item(&recipe.Result)
w.String(&recipe.Block)
w.Varuint32(&recipe.RecipeNetworkID)
}
// Unmarshal ...
func (recipe *SmithingTransformRecipe) Unmarshal(r *Reader) {
r.String(&recipe.RecipeID)
r.ItemDescriptorCount(&recipe.Template)
r.ItemDescriptorCount(&recipe.Base)
r.ItemDescriptorCount(&recipe.Addition)
r.Item(&recipe.Result)
r.String(&recipe.Block)
r.Varuint32(&recipe.RecipeNetworkID)
}
// FromLatest ...
func (recipe *SmithingTrimRecipe) FromLatest(v protocol.SmithingTrimRecipe) *SmithingTrimRecipe {
recipe.RecipeNetworkID = v.RecipeNetworkID
recipe.RecipeID = v.RecipeID
recipe.Template = v.Template
recipe.Base = v.Base
recipe.Addition = v.Addition
recipe.Block = v.Block
return recipe
}
// ToLatest ...
func (recipe *SmithingTrimRecipe) ToLatest() protocol.SmithingTrimRecipe {
return protocol.SmithingTrimRecipe{
RecipeNetworkID: recipe.RecipeNetworkID,
RecipeID: recipe.RecipeID,
Template: recipe.Template,
Base: recipe.Base,
Addition: recipe.Addition,
Block: recipe.Block,
}
}
// Marshal ...
func (recipe *SmithingTrimRecipe) Marshal(w *Writer) {
w.String(&recipe.RecipeID)
w.ItemDescriptorCount(&recipe.Template)
w.ItemDescriptorCount(&recipe.Base)
w.ItemDescriptorCount(&recipe.Addition)
w.String(&recipe.Block)
w.Varuint32(&recipe.RecipeNetworkID)
}
// Unmarshal ...
func (recipe *SmithingTrimRecipe) Unmarshal(r *Reader) {
r.String(&recipe.RecipeID)
r.ItemDescriptorCount(&recipe.Template)
r.ItemDescriptorCount(&recipe.Base)
r.ItemDescriptorCount(&recipe.Addition)
r.String(&recipe.Block)
r.Varuint32(&recipe.RecipeNetworkID)
}
// marshalShaped ...
func marshalShaped(r protocol.IO, recipe *ShapedRecipe) {
r.String(&recipe.RecipeID)
r.Varint32(&recipe.Width)
r.Varint32(&recipe.Height)
protocol.FuncSliceOfLen(r, uint32(recipe.Width*recipe.Height), &recipe.Input, r.ItemDescriptorCount)
protocol.FuncSlice(r, &recipe.Output, r.Item)
r.UUID(&recipe.UUID)
r.String(&recipe.Block)
r.Varint32(&recipe.Priority)
r.Bool(&recipe.AssumeSymmetry)
if IsProtoGTE(r, ID685) {
protocol.Single(r, &recipe.UnlockRequirement)
}
r.Varuint32(&recipe.RecipeNetworkID)
}
// marshalShapeless ...
func marshalShapeless(r protocol.IO, recipe *ShapelessRecipe) {
r.String(&recipe.RecipeID)
protocol.FuncSlice(r, &recipe.Input, r.ItemDescriptorCount)
protocol.FuncSlice(r, &recipe.Output, r.Item)
r.UUID(&recipe.UUID)
r.String(&recipe.Block)
r.Varint32(&recipe.Priority)
if IsProtoGTE(r, ID685) {
protocol.Single(r, &recipe.UnlockRequirement)
}
r.Varuint32(&recipe.RecipeNetworkID)
}

View File

@@ -76,6 +76,16 @@ func convertPacketFunc(pid uint32, cur func() packet.Packet) func() packet.Packe
return func() packet.Packet { return &legacypacket.InventoryTransaction{} } return func() packet.Packet { return &legacypacket.InventoryTransaction{} }
case packet.IDItemStackRequest: case packet.IDItemStackRequest:
return func() packet.Packet { return &legacypacket.ItemStackRequest{} } return func() packet.Packet { return &legacypacket.ItemStackRequest{} }
case packet.IDCraftingData:
return func() packet.Packet { return &legacypacket.CraftingData{} }
case packet.IDContainerClose:
return func() packet.Packet { return &legacypacket.ContainerClose{} }
case packet.IDText:
return func() packet.Packet { return &legacypacket.Text{} }
case packet.IDStartGame:
return func() packet.Packet { return &legacypacket.StartGame{} }
case packet.IDCodeBuilderSource:
return func() packet.Packet { return &legacypacket.CodeBuilderSource{} }
default: default:
return cur return cur
} }
@@ -127,6 +137,8 @@ func (p *Protocol) ConvertFromLatest(pk packet.Packet, conn *minecraft.Conn) []p
func (p *Protocol) downgradePackets(pks []packet.Packet, conn *minecraft.Conn) []packet.Packet { func (p *Protocol) downgradePackets(pks []packet.Packet, conn *minecraft.Conn) []packet.Packet {
for pkIndex, pk := range pks { for pkIndex, pk := range pks {
switch pk := pk.(type) { switch pk := pk.(type) {
case *packet.ClientCacheStatus:
pk.Enabled = false // TODO: enable when chunk translation is not broken
case *packet.CameraPresets: case *packet.CameraPresets:
presets := make([]proto.CameraPreset, len(pk.Presets)) presets := make([]proto.CameraPreset, len(pk.Presets))
for i, p := range pk.Presets { for i, p := range pk.Presets {
@@ -135,9 +147,6 @@ func (p *Protocol) downgradePackets(pks []packet.Packet, conn *minecraft.Conn) [
pks[pkIndex] = &legacypacket.CameraPresets{ pks[pkIndex] = &legacypacket.CameraPresets{
Presets: presets, Presets: presets,
} }
case *packet.StartGame:
pk.GameVersion = p.ver
pk.BaseGameVersion = p.ver
case *packet.PlayerAuthInput: case *packet.PlayerAuthInput:
inputData := pk.InputData inputData := pk.InputData
if p.ID() < proto.ID766 { if p.ID() < proto.ID766 {
@@ -398,6 +407,126 @@ func (p *Protocol) downgradePackets(pks []packet.Packet, conn *minecraft.Conn) [
requests[i] = (&proto.ItemStackRequest{}).FromLatest(r) requests[i] = (&proto.ItemStackRequest{}).FromLatest(r)
} }
pks[pkIndex] = &legacypacket.ItemStackRequest{Requests: requests} pks[pkIndex] = &legacypacket.ItemStackRequest{Requests: requests}
case *packet.Text:
pks[pkIndex] = &legacypacket.Text{
TextType: pk.TextType,
NeedsTranslation: pk.NeedsTranslation,
SourceName: pk.SourceName,
Message: pk.Message,
Parameters: pk.Parameters,
XUID: pk.XUID,
PlatformChatID: pk.PlatformChatID,
FilteredMessage: pk.FilteredMessage,
}
case *packet.ContainerClose:
pks[pkIndex] = &legacypacket.ContainerClose{
WindowID: pk.WindowID,
ContainerType: pk.ContainerType,
ServerSide: pk.ServerSide,
}
case *packet.CraftingData:
recipes := make([]proto.Recipe, len(pk.Recipes))
for i, r := range pk.Recipes {
recipes[i] = proto.RecipeFromLatest(r)
}
pks[pkIndex] = &legacypacket.CraftingData{
Recipes: recipes,
PotionRecipes: pk.PotionRecipes,
PotionContainerChangeRecipes: pk.PotionContainerChangeRecipes,
MaterialReducers: pk.MaterialReducers,
ClearRecipes: pk.ClearRecipes,
}
case *packet.StartGame:
// Adjust game version
pk.GameVersion = p.ver
pk.BaseGameVersion = p.ver
pks[pkIndex] = &legacypacket.StartGame{
EntityUniqueID: pk.EntityUniqueID,
EntityRuntimeID: pk.EntityRuntimeID,
PlayerGameMode: pk.PlayerGameMode,
PlayerPosition: pk.PlayerPosition,
Pitch: pk.Pitch,
Yaw: pk.Yaw,
WorldSeed: pk.WorldSeed,
SpawnBiomeType: pk.SpawnBiomeType,
UserDefinedBiomeName: pk.UserDefinedBiomeName,
Dimension: pk.Dimension,
Generator: pk.Generator,
WorldGameMode: pk.WorldGameMode,
Hardcore: pk.Hardcore,
Difficulty: pk.Difficulty,
WorldSpawn: pk.WorldSpawn,
AchievementsDisabled: pk.AchievementsDisabled,
EditorWorldType: pk.EditorWorldType,
CreatedInEditor: pk.CreatedInEditor,
ExportedFromEditor: pk.ExportedFromEditor,
DayCycleLockTime: pk.DayCycleLockTime,
EducationEditionOffer: pk.EducationEditionOffer,
EducationFeaturesEnabled: pk.EducationFeaturesEnabled,
EducationProductID: pk.EducationProductID,
RainLevel: pk.RainLevel,
LightningLevel: pk.LightningLevel,
ConfirmedPlatformLockedContent: pk.ConfirmedPlatformLockedContent,
MultiPlayerGame: pk.MultiPlayerGame,
LANBroadcastEnabled: pk.LANBroadcastEnabled,
XBLBroadcastMode: pk.XBLBroadcastMode,
PlatformBroadcastMode: pk.PlatformBroadcastMode,
CommandsEnabled: pk.CommandsEnabled,
TexturePackRequired: pk.TexturePackRequired,
GameRules: pk.GameRules,
Experiments: pk.Experiments,
ExperimentsPreviouslyToggled: pk.ExperimentsPreviouslyToggled,
BonusChestEnabled: pk.BonusChestEnabled,
StartWithMapEnabled: pk.StartWithMapEnabled,
PlayerPermissions: pk.PlayerPermissions,
ServerChunkTickRadius: pk.ServerChunkTickRadius,
HasLockedBehaviourPack: pk.HasLockedBehaviourPack,
HasLockedTexturePack: pk.HasLockedTexturePack,
FromLockedWorldTemplate: pk.FromLockedWorldTemplate,
MSAGamerTagsOnly: pk.MSAGamerTagsOnly,
FromWorldTemplate: pk.FromWorldTemplate,
WorldTemplateSettingsLocked: pk.WorldTemplateSettingsLocked,
OnlySpawnV1Villagers: pk.OnlySpawnV1Villagers,
PersonaDisabled: pk.PersonaDisabled,
CustomSkinsDisabled: pk.CustomSkinsDisabled,
EmoteChatMuted: pk.EmoteChatMuted,
BaseGameVersion: pk.BaseGameVersion,
LimitedWorldWidth: pk.LimitedWorldWidth,
LimitedWorldDepth: pk.LimitedWorldDepth,
NewNether: pk.NewNether,
EducationSharedResourceURI: pk.EducationSharedResourceURI,
ForceExperimentalGameplay: pk.ForceExperimentalGameplay,
LevelID: pk.LevelID,
WorldName: pk.WorldName,
TemplateContentIdentity: pk.TemplateContentIdentity,
Trial: pk.Trial,
PlayerMovementSettings: pk.PlayerMovementSettings,
Time: pk.Time,
EnchantmentSeed: pk.EnchantmentSeed,
Blocks: pk.Blocks,
Items: pk.Items,
MultiPlayerCorrelationID: pk.MultiPlayerCorrelationID,
ServerAuthoritativeInventory: pk.ServerAuthoritativeInventory,
GameVersion: pk.GameVersion,
PropertyData: pk.PropertyData,
ServerBlockStateChecksum: pk.ServerBlockStateChecksum,
ClientSideGeneration: pk.ClientSideGeneration,
WorldTemplateID: pk.WorldTemplateID,
ChatRestrictionLevel: pk.ChatRestrictionLevel,
DisablePlayerInteractions: pk.DisablePlayerInteractions,
ServerID: pk.ServerID,
WorldID: pk.WorldID,
ScenarioID: pk.ScenarioID,
UseBlockNetworkIDHashes: pk.UseBlockNetworkIDHashes,
ServerAuthoritativeSound: pk.ServerAuthoritativeSound,
}
case *packet.CodeBuilderSource:
pks[pkIndex] = &legacypacket.CodeBuilderSource{
Operation: pk.Operation,
Category: pk.Category,
CodeStatus: pk.CodeStatus,
}
} }
} }
@@ -674,6 +803,122 @@ func (p *Protocol) upgradePackets(pks []packet.Packet, conn *minecraft.Conn) []p
requests[i] = r.ToLatest() requests[i] = r.ToLatest()
} }
pks[pkIndex] = &packet.ItemStackRequest{Requests: requests} pks[pkIndex] = &packet.ItemStackRequest{Requests: requests}
case *legacypacket.CraftingData:
recipes := make([]protocol.Recipe, len(pk.Recipes))
for i, r := range pk.Recipes {
recipes[i] = proto.RecipeToLatest(r)
}
pks[pkIndex] = &packet.CraftingData{
Recipes: recipes,
PotionRecipes: pk.PotionRecipes,
PotionContainerChangeRecipes: pk.PotionContainerChangeRecipes,
MaterialReducers: pk.MaterialReducers,
ClearRecipes: pk.ClearRecipes,
}
case *legacypacket.ContainerClose:
pks[pkIndex] = &packet.ContainerClose{
WindowID: pk.WindowID,
ContainerType: pk.ContainerType,
ServerSide: pk.ServerSide,
}
case *legacypacket.Text:
pks[pkIndex] = &packet.Text{
TextType: pk.TextType,
NeedsTranslation: pk.NeedsTranslation,
SourceName: pk.SourceName,
Message: pk.Message,
Parameters: pk.Parameters,
XUID: pk.XUID,
PlatformChatID: pk.PlatformChatID,
FilteredMessage: pk.FilteredMessage,
}
case *legacypacket.StartGame:
pks[pkIndex] = &packet.StartGame{
EntityUniqueID: pk.EntityUniqueID,
EntityRuntimeID: pk.EntityRuntimeID,
PlayerGameMode: pk.PlayerGameMode,
PlayerPosition: pk.PlayerPosition,
Pitch: pk.Pitch,
Yaw: pk.Yaw,
WorldSeed: pk.WorldSeed,
SpawnBiomeType: pk.SpawnBiomeType,
UserDefinedBiomeName: pk.UserDefinedBiomeName,
Dimension: pk.Dimension,
Generator: pk.Generator,
WorldGameMode: pk.WorldGameMode,
Hardcore: pk.Hardcore,
Difficulty: pk.Difficulty,
WorldSpawn: pk.WorldSpawn,
AchievementsDisabled: pk.AchievementsDisabled,
EditorWorldType: pk.EditorWorldType,
CreatedInEditor: pk.CreatedInEditor,
ExportedFromEditor: pk.ExportedFromEditor,
DayCycleLockTime: pk.DayCycleLockTime,
EducationEditionOffer: pk.EducationEditionOffer,
EducationFeaturesEnabled: pk.EducationFeaturesEnabled,
EducationProductID: pk.EducationProductID,
RainLevel: pk.RainLevel,
LightningLevel: pk.LightningLevel,
ConfirmedPlatformLockedContent: pk.ConfirmedPlatformLockedContent,
MultiPlayerGame: pk.MultiPlayerGame,
LANBroadcastEnabled: pk.LANBroadcastEnabled,
XBLBroadcastMode: pk.XBLBroadcastMode,
PlatformBroadcastMode: pk.PlatformBroadcastMode,
CommandsEnabled: pk.CommandsEnabled,
TexturePackRequired: pk.TexturePackRequired,
GameRules: pk.GameRules,
Experiments: pk.Experiments,
ExperimentsPreviouslyToggled: pk.ExperimentsPreviouslyToggled,
BonusChestEnabled: pk.BonusChestEnabled,
StartWithMapEnabled: pk.StartWithMapEnabled,
PlayerPermissions: pk.PlayerPermissions,
ServerChunkTickRadius: pk.ServerChunkTickRadius,
HasLockedBehaviourPack: pk.HasLockedBehaviourPack,
HasLockedTexturePack: pk.HasLockedTexturePack,
FromLockedWorldTemplate: pk.FromLockedWorldTemplate,
MSAGamerTagsOnly: pk.MSAGamerTagsOnly,
FromWorldTemplate: pk.FromWorldTemplate,
WorldTemplateSettingsLocked: pk.WorldTemplateSettingsLocked,
OnlySpawnV1Villagers: pk.OnlySpawnV1Villagers,
PersonaDisabled: pk.PersonaDisabled,
CustomSkinsDisabled: pk.CustomSkinsDisabled,
EmoteChatMuted: pk.EmoteChatMuted,
BaseGameVersion: pk.BaseGameVersion,
LimitedWorldWidth: pk.LimitedWorldWidth,
LimitedWorldDepth: pk.LimitedWorldDepth,
NewNether: pk.NewNether,
EducationSharedResourceURI: pk.EducationSharedResourceURI,
ForceExperimentalGameplay: pk.ForceExperimentalGameplay,
LevelID: pk.LevelID,
WorldName: pk.WorldName,
TemplateContentIdentity: pk.TemplateContentIdentity,
Trial: pk.Trial,
PlayerMovementSettings: pk.PlayerMovementSettings,
Time: pk.Time,
EnchantmentSeed: pk.EnchantmentSeed,
Blocks: pk.Blocks,
Items: pk.Items,
MultiPlayerCorrelationID: pk.MultiPlayerCorrelationID,
ServerAuthoritativeInventory: pk.ServerAuthoritativeInventory,
GameVersion: pk.GameVersion,
PropertyData: pk.PropertyData,
ServerBlockStateChecksum: pk.ServerBlockStateChecksum,
ClientSideGeneration: pk.ClientSideGeneration,
WorldTemplateID: pk.WorldTemplateID,
ChatRestrictionLevel: pk.ChatRestrictionLevel,
DisablePlayerInteractions: pk.DisablePlayerInteractions,
ServerID: pk.ServerID,
WorldID: pk.WorldID,
ScenarioID: pk.ScenarioID,
UseBlockNetworkIDHashes: pk.UseBlockNetworkIDHashes,
ServerAuthoritativeSound: pk.ServerAuthoritativeSound,
}
case *legacypacket.CodeBuilderSource:
pks[pkIndex] = &packet.CodeBuilderSource{
Operation: pk.Operation,
Category: pk.Category,
CodeStatus: pk.CodeStatus,
}
} }
} }
return pks return pks

37
legacyver/v671.go Normal file
View File

@@ -0,0 +1,37 @@
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 (
// ItemVersion671 ...
ItemVersion671 = 181
// BlockVersion671 ...
BlockVersion671 int32 = (1 << 24) | (20 << 16) | (80 << 8)
)
var (
//go:embed data/item_runtime_ids_671.nbt
itemRuntimeIDData671 []byte
//go:embed data/required_item_list_671.json
requiredItemList671 []byte
//go:embed data/block_states_671.nbt
blockStateData671 []byte
)
// New671 ...
func New671() *Protocol {
itemMapping := mapping.NewItemMapping(itemRuntimeIDData671, requiredItemList671, ItemVersion671, false)
blockMapping := mapping.NewBlockMapping(blockStateData671)
return &Protocol{
ver: "1.20.80",
id: proto.ID671,
blockTranslator: NewBlockTranslator(blockMapping, latestBlockMapping, chunk.NewNetworkPersistentEncoding(blockMapping, BlockVersion671), chunk.NewBlockPaletteEncoding(blockMapping, BlockVersion671), false),
itemTranslator: NewItemTranslator(itemMapping, itemMappingLatest, blockMapping, blockMappingLatest),
}
}

View File

@@ -15,7 +15,7 @@ const (
) )
// New685 uses same data as 686 // New685 uses same data as 686
func New685(direct bool) *Protocol { func New685() *Protocol {
itemMapping := mapping.NewItemMapping(itemRuntimeIDData686, requiredItemList686, ItemVersion685, false) itemMapping := mapping.NewItemMapping(itemRuntimeIDData686, requiredItemList686, ItemVersion685, false)
blockMapping := mapping.NewBlockMapping(blockStateData686) blockMapping := mapping.NewBlockMapping(blockStateData686)

View File

@@ -23,7 +23,7 @@ var (
blockStateData686 []byte blockStateData686 []byte
) )
func New686(direct bool) *Protocol { func New686() *Protocol {
itemMapping := mapping.NewItemMapping(itemRuntimeIDData686, requiredItemList686, ItemVersion686, false) itemMapping := mapping.NewItemMapping(itemRuntimeIDData686, requiredItemList686, ItemVersion686, false)
blockMapping := mapping.NewBlockMapping(blockStateData686) blockMapping := mapping.NewBlockMapping(blockStateData686)

View File

@@ -23,7 +23,7 @@ var (
blockStateData712 []byte blockStateData712 []byte
) )
func New712(direct bool) *Protocol { func New712() *Protocol {
itemMapping := mapping.NewItemMapping(itemRuntimeIDData712, requiredItemList712, ItemVersion712, false) itemMapping := mapping.NewItemMapping(itemRuntimeIDData712, requiredItemList712, ItemVersion712, false)
blockMapping := mapping.NewBlockMapping(blockStateData712) blockMapping := mapping.NewBlockMapping(blockStateData712)

View File

@@ -23,7 +23,7 @@ var (
blockStateData729 []byte blockStateData729 []byte
) )
func New729(direct bool) *Protocol { func New729() *Protocol {
itemMapping := mapping.NewItemMapping(itemRuntimeIDData729, requiredItemList729, ItemVersion729, false) itemMapping := mapping.NewItemMapping(itemRuntimeIDData729, requiredItemList729, ItemVersion729, false)
blockMapping := mapping.NewBlockMapping(blockStateData729) blockMapping := mapping.NewBlockMapping(blockStateData729)

View File

@@ -23,7 +23,7 @@ var (
blockStateData748 []byte blockStateData748 []byte
) )
func New748(direct bool) *Protocol { func New748() *Protocol {
itemMapping := mapping.NewItemMapping(itemRuntimeIDData748, requiredItemList748, ItemVersion748, false) itemMapping := mapping.NewItemMapping(itemRuntimeIDData748, requiredItemList748, ItemVersion748, false)
blockMapping := mapping.NewBlockMapping(blockStateData748) blockMapping := mapping.NewBlockMapping(blockStateData748)