package main import ( "bufio" "bytes" "errors" "github.com/Tnze/go-mc/nbt" "github.com/google/uuid" "math/rand/v2" "time" ) func (player *Player) sendBundleDelimiter() error { return player.sendPacket(0x00, &bytes.Buffer{}) } func (player *Player) sendSpawnEntity(entity *Entity) error { var buf bytes.Buffer entity.add(&buf) return player.sendPacket(0x01, &buf) } func (player *Player) sendSpawnXP(xpOrb XPOrb) error { var buf bytes.Buffer xpOrb.add(&buf) return player.sendPacket(0x02, &buf) } func (player *Player) sendEntityAnimation(entity *Entity) error { var buf bytes.Buffer addVarint(&buf, entity.ID) addByte(&buf, entity.Animation) return player.sendPacket(0x03, &buf) } func (player *Player) sendAwardStatistics() error { var buf bytes.Buffer addVarint(&buf, int32(len(player.statistics))) for _, stat := range player.statistics { addVarint(&buf, stat.CategoryID) addVarint(&buf, stat.StatisticID) addVarint(&buf, stat.Value) } return player.sendPacket(0x04, &buf) } func (player *Player) sendBlockChangeACK(seqID int32) error { var buf bytes.Buffer addVarint(&buf, seqID) return player.sendPacket(0x05, &buf) } func (player *Player) sendBlockDestroyStage(entity *Entity, block *Block) error { var buf bytes.Buffer addVarint(&buf, entity.ID) block.Position.add(&buf) addByte(&buf, block.DestroyStage) return player.sendPacket(0x06, &buf) } func (player *Player) sendBlockEntityData(entity *Entity, block *Block) error { var buf bytes.Buffer block.Position.add(&buf) addVarint(&buf, entity.ID) var nbtBuf bytes.Buffer nbtWriter := bufio.NewWriter(&nbtBuf) nbtEnc := nbt.NewEncoder(nbtWriter) for key, val := range block.BlockEntityData { err := nbtEnc.Encode(val, key) if err != nil { return err } } nbtWriter.Flush() buf.ReadFrom(&nbtBuf) return player.sendPacket(0x07, &buf) } func (player *Player) sendBlockAction(block *Block, actionID uint8, actionParam uint8) error { var buf bytes.Buffer block.Position.add(&buf) addByte(&buf, actionID) addByte(&buf, actionParam) addVarint(&buf, block.BlockTypeID) return player.sendPacket(0x08, &buf) } func (player *Player) sendBlockUpdate(block *Block) error { var buf bytes.Buffer block.Position.add(&buf) addVarint(&buf, block.BlockStateID) return player.sendPacket(0x09, &buf) } func (player *Player) sendDifficulty() error { var buf bytes.Buffer addByte(&buf, world.Difficulty) addBool(&buf, world.DifficultyLocked) return player.sendPacket(0x0B, &buf) } func (player *Player) sendChunks() error { //var buf bytes.Buffer var bufFinished bytes.Buffer err := player.sendPacket(0x0D, &bytes.Buffer{}) //chunk batch start if err != nil { return err } //count:= int32(len(chunks)) count := int32(0) addVarint(&bufFinished, count) err = player.sendPacket(0x0C, &bufFinished) //chunk batch end if err != nil { return err } return nil } //0x0E is chunk biomes, might implement later //0x10 is Command Suggestions Response, might implement later //0x11 is Commands, might implement later func (player *Player) sendCloseContainer(windowID uint8) error { var buf bytes.Buffer addByte(&buf, windowID) player.WindowID = 0 return player.sendPacket(0x12, &buf) } func (player *Player) sendContainerContent(window *Window, cursor *ItemStack) error { var buf bytes.Buffer addByte(&buf, window.ID) addVarint(&buf, player.lastContainerStateID) player.lastContainerStateID++ addVarint(&buf, int32(len(window.Slots))) for _, slot := range window.Slots { slot.Stack.add(&buf) } cursor.add(&buf) return player.sendPacket(0x13, &buf) } func (player *Player) sendContainerProperty(windowID uint8, property int16, value int16) error { var buf bytes.Buffer addByte(&buf, windowID) addInt16(&buf, property) addInt16(&buf, value) return player.sendPacket(0x14, &buf) } func (player *Player) sendContainerSlot(windowID uint8, slotIndex int16, slot *ItemStack) error { var buf bytes.Buffer addByte(&buf, windowID) addVarint(&buf, player.lastContainerStateID) player.lastContainerStateID++ addInt16(&buf, slotIndex) slot.add(&buf) return player.sendPacket(0x15, &buf) } //0x16 is cookie, might get implemented cross-state func (player *Player) sendCooldown(itemID int32, cooldownTicks int32) error { var buf bytes.Buffer addVarint(&buf, itemID) addVarint(&buf, cooldownTicks) return player.sendPacket(0x17, &buf) } func (player *Player) sendChatSuggestions(action int32, entries []string) error { var buf bytes.Buffer addVarint(&buf, action) addVarint(&buf, int32(len(entries))) for _, entry := range entries { addString(&buf, entry) } return player.sendPacket(0x18, &buf) } //0x19 is plugin message, should be implemented globally func (player *Player) sendDamageEvent(damagedEntity *Entity, damageType int32, sourceEntity *Entity, sourceDirectEntity *Entity) error { var buf bytes.Buffer addVarint(&buf, damagedEntity.ID) addVarint(&buf, damageType) addVarint(&buf, sourceEntity.ID) if sourceDirectEntity != nil { addVarint(&buf, sourceDirectEntity.ID) } else { addVarint(&buf, sourceEntity.ID) } addBool(&buf, true) sourceEntity.Position.add(&buf) return player.sendPacket(0x1A, &buf) } func (player *Player) sendDebugSample(sampleType int32, sample []int64) error { var buf bytes.Buffer addVarint(&buf, int32(len(sample))) for _, val := range sample { addInt64(&buf, val) } addVarint(&buf, sampleType) return player.sendPacket(0x1B, &buf) } func (player *Player) deleteMessage(messageID int32, signature []byte) error { var buf bytes.Buffer addVarint(&buf, messageID) if messageID == 0 { for _, sigPiece := range signature { addByte(&buf, sigPiece) } } return player.sendPacket(0x1C, &buf) } //0x1D is disconnect, should be implemented globally func (player *Player) sendDisguisedChatMessage(message TextComponent, typeID int32, senderName TextComponent, targetName *TextComponent) error { var buf bytes.Buffer addTextComponent(&buf, message) addVarint(&buf, typeID) addTextComponent(&buf, senderName) hasTargetName := targetName != nil addBool(&buf, hasTargetName) if hasTargetName { addTextComponent(&buf, *targetName) } return player.sendPacket(0x1E, &buf) } func (player *Player) sendEntityEvent(entity *Entity) error { var buf bytes.Buffer addInt32(&buf, entity.ID) addByte(&buf, byte(entity.status)) return player.sendPacket(0x1F, &buf) } func (player *Player) sendExplosion(explosion *Explosion) error { var buf bytes.Buffer explosion.Position.add(&buf) addFloat32(&buf, explosion.Strength) addVarint(&buf, int32(len(explosion.AffectedBlockRecords))) for _, affectedBlockRecord := range explosion.AffectedBlockRecords { affectedBlockRecord.add(&buf) } explosion.PlayerMotion.add(&buf) addVarint(&buf, explosion.BlockInteraction) explosion.SmallExplosionParticle.addExplosion(&buf) explosion.LargeExplosionParticle.addExplosion(&buf) explosion.ExplosionSound.add(&buf) return player.sendPacket(0x20, &buf) } func (player *Player) sendUnloadChunk(chunk *Chunk) error { var buf bytes.Buffer addInt32(&buf, chunk.Z) addInt32(&buf, chunk.X) return player.sendPacket(0x21, &buf) } func (player *Player) sendGameEvent(event uint8, value float32) error { var buf bytes.Buffer addByte(&buf, event) addFloat32(&buf, value) return player.sendPacket(0x22, &buf) } func (player *Player) sendHorseScreen(windowID uint8, slotCount int32, entityID int32) error { var buf bytes.Buffer addByte(&buf, windowID) addVarint(&buf, slotCount) addInt32(&buf, entityID) return player.sendPacket(0x23, &buf) } // Example usage in sendHurtAnimation func (player *Player) sendHurtAnimation(attacker *Entity) error { var buf bytes.Buffer addVarint(&buf, attacker.ID) // Calculate yaw based on attacker and player positions attackYaw := calculateYaw(attacker.Position, player.Position) addFloat32(&buf, attackYaw) return player.sendPacket(0x24, &buf) } func (player *Player) sendWorldBorder() error { var buf bytes.Buffer addFloat64(&buf, player.Dimension.Border.X) addFloat64(&buf, player.Dimension.Border.Z) addFloat64(&buf, player.Dimension.Border.OldDiameter) addFloat64(&buf, player.Dimension.Border.NewDiameter) addVarlong(&buf, player.Dimension.Border.Speed) addVarint(&buf, player.Dimension.Border.PortalTeleportBoundary) addVarint(&buf, player.Dimension.Border.WarningBlocks) addVarint(&buf, player.Dimension.Border.WarningTime) return player.sendPacket(0x25, &buf) } //0x26 is keepalive //0x27 is chunk and light //TODO LATER func (player *Player) sendWorldEvent(event int32, position BlockPosition, data int32, disableRelativeVolume bool) error { var buf bytes.Buffer addInt32(&buf, event) position.add(&buf) addInt32(&buf, data) addBool(&buf, disableRelativeVolume) return player.sendPacket(0x28, &buf) } func (player *Player) sendParticle(particle *Particle, count int32) error { var buf bytes.Buffer particle.addPacket(&buf, count) return player.sendPacket(0x29, &buf) } //0x2A is Update light levels //TODO LATER func (player *Player) sendPlayStart() error { var buf bytes.Buffer addInt32(&buf, player.Entity.ID) addBool(&buf, world.Hardcore) addVarint(&buf, int32(len(world.Dimensions))) for _, dimension := range world.Dimensions { addString(&buf, dimension.Name) } addVarint(&buf, maxPlayers) addVarint(&buf, world.ViewDistance) addVarint(&buf, world.SimulationDistance) addBool(&buf, world.ReducedDebugInfo) addBool(&buf, world.EnableRespawnScreen) addBool(&buf, world.DoLimitedCrafting) addVarint(&buf, player.Dimension.ID) addString(&buf, player.Dimension.Name) noise, err := world.Seed.MarshalBinary() if err != nil { return err } for i := 0; i < 8; i++ { addByte(&buf, noise[i]) } addByte(&buf, player.GameMode) addByte(&buf, byte(player.PreviousGameMode)) addBool(&buf, world.Debug) addBool(&buf, world.Flat) hasDeathLocation := player.DeathDimension != nil addBool(&buf, hasDeathLocation) if hasDeathLocation { addString(&buf, player.DeathDimension.Name) player.DeathPosition.add(&buf) } addVarint(&buf, player.Entity.PortalCooldown) addBool(&buf, world.EnforceSecureChat) return player.sendPacket(0x2B, &buf) } func (player *Player) sendMapItemData(mapId int32) error { var buf bytes.Buffer mapEntry, found := world.Maps[mapId] if found { mapEntry.add(&buf) } return player.sendPacket(0x2C, &buf) } func (player *Player) sendMerchantOffers(windowID int32, entity *VillagerEntity) error { var buf bytes.Buffer entity.addOffers(&buf, windowID) return player.sendPacket(0x2D, &buf) } func (player *Player) sendEntityPositionUpdate(entity *Entity, deltas []int16) error { if len(deltas) != 3 { return errors.New("invalid deltas") } var buf bytes.Buffer addVarint(&buf, entity.ID) addInt16(&buf, deltas[0]) addInt16(&buf, deltas[1]) addInt16(&buf, deltas[2]) addBool(&buf, entity.onGround()) return player.sendPacket(0x2E, &buf) } func (player *Player) sendEntityPositionRotationUpdate(entity *Entity, deltas []int16) error { if len(deltas) != 3 { return errors.New("invalid deltas") } var buf bytes.Buffer addVarint(&buf, entity.ID) addInt16(&buf, deltas[0]) addInt16(&buf, deltas[1]) addInt16(&buf, deltas[2]) addByte(&buf, entity.Rotation.Yaw) addByte(&buf, entity.Rotation.Pitch) addBool(&buf, entity.onGround()) return player.sendPacket(0x2F, &buf) } func (player *Player) sendEntityRotationUpdate(entity *Entity) error { var buf bytes.Buffer addVarint(&buf, entity.ID) addByte(&buf, entity.Rotation.Yaw) addByte(&buf, entity.Rotation.Pitch) addBool(&buf, entity.onGround()) return player.sendPacket(0x30, &buf) } func (player *Player) sendVehicleMove() error { if player.Entity.Vehicle != nil { var buf bytes.Buffer player.Entity.Vehicle.Position.add(&buf) addFloat32(&buf, float32(player.Entity.Vehicle.Rotation.Yaw)) addFloat32(&buf, float32(player.Entity.Vehicle.Rotation.Pitch)) return player.sendPacket(0x31, &buf) } return errors.New("no vehicle") } func (player *Player) sendBookOpen(isOffHand bool) error { var buf bytes.Buffer hand := int32(0) if isOffHand { hand = 1 } addVarint(&buf, hand) return player.sendPacket(0x32, &buf) } func (player *Player) sendOpenScreen(windowID int32, window *Window, title TextComponent) error { var buf bytes.Buffer addVarint(&buf, windowID) addVarint(&buf, window.Type) addTextComponent(&buf, title) return player.sendPacket(0x33, &buf) } func (player *Player) sendOpenSignEditor(blockPosition BlockPosition, front bool) error { var buf bytes.Buffer blockPosition.add(&buf) addBool(&buf, front) return player.sendPacket(0x34, &buf) } //0x35 is ping to client //0x36 is pong func (player *Player) sendGhostRecipe(windowID uint8, recipe string) error { var buf bytes.Buffer addByte(&buf, windowID) addString(&buf, recipe) return player.sendPacket(0x37, &buf) } func (player *Player) sendAbilities() error { var buf bytes.Buffer addByte(&buf, player.Abilites.export()) addFloat32(&buf, player.FlyingSpeed) addFloat32(&buf, player.FOVModifier) return player.sendPacket(0x38, &buf) } func (player *Player) sendChatMessage(sender *Player, message string) error { var buf bytes.Buffer //TODO IMPLEMENT addUUID(&buf, sender.uuid) addVarint(&buf, sender.MessageIndex) addBool(&buf, false) addString(&buf, message) addInt64(&buf, time.Now().Unix()) addInt64(&buf, rand.Int64()) //not implementing secure chat return player.sendPacket(0x39, &buf) } func (player *Player) sendDeath(message TextComponent) error { var buf bytes.Buffer addVarint(&buf, player.Entity.ID) addTextComponent(&buf, message) return player.sendPacket(0x3C, &buf) } func (player *Player) sendRemoveFromTab(playersToRemove []uuid.UUID) error { if len(playersToRemove) > 0 { var buf bytes.Buffer addVarint(&buf, int32(len(playersToRemove))) for _, playerToRemove := range playersToRemove { addUUID(&buf, playerToRemove) } return player.sendPacket(0x3D, &buf) } return errors.New("no playersToRemove") } func (player *Player) sendPlayerInfoUpdate(actions byte, players []uuid.UUID, playerActions []PlayerAction) error { var buf bytes.Buffer // Write the actions byte addByte(&buf, actions) // Write the number of players addVarint(&buf, int32(len(players))) // For each player, write their UUID and corresponding actions for i, playerUUID := range players { addUUID(&buf, playerUUID) action := playerActions[i] // Based on the action mask, we add the relevant player info if actions&0x01 != 0 { action.add(&buf) } if actions&0x04 != 0 { addVarint(&buf, action.GameMode) // Update Game Mode } if actions&0x08 != 0 { addBool(&buf, action.Listed) // Update Listed } if actions&0x10 != 0 { addVarint(&buf, action.Ping) // Update Latency } if actions&0x20 != 0 { addBool(&buf, action.HasDisplayName) // Update Display Name if action.HasDisplayName { addString(&buf, action.DisplayName) } } } return player.sendPacket(0x3E, &buf) } func (player *Player) sendLookAtPos(pos EntityPosition, aimEyes bool) error { var buf bytes.Buffer addBool(&buf, aimEyes) pos.add(&buf) addBool(&buf, false) return player.sendPacket(0x3F, &buf) } func (player *Player) sendLookAtEntity(entity *Entity, atEntityEyes bool, aimEyes bool) error { var buf bytes.Buffer addBool(&buf, aimEyes) entity.Position.add(&buf) addBool(&buf, true) addVarint(&buf, entity.ID) addBool(&buf, atEntityEyes) return player.sendPacket(0x3F, &buf) } func (player *Player) sendPosition() error { var buf bytes.Buffer player.Position.add(&buf) player.Rotation.addBasic(&buf) addByte(&buf, 0) //all absolute addVarint(&buf, player.teleportID) player.teleportID++ return player.sendPacket(0x40, &buf) } func (player *Player) sendUpdateRecipeBook(action int32, craftingOpen, craftingFilter, smeltingOpen, smeltingFilter, blastFurnaceOpen, blastFurnaceFilter, smokerOpen, smokerFilter bool, recipeIDs1 []string, recipeIDs2 []string) error { var buf bytes.Buffer // Add Action addVarint(&buf, action) // Add boolean flags addBool(&buf, craftingOpen) addBool(&buf, craftingFilter) addBool(&buf, smeltingOpen) addBool(&buf, smeltingFilter) addBool(&buf, blastFurnaceOpen) addBool(&buf, blastFurnaceFilter) addBool(&buf, smokerOpen) addBool(&buf, smokerFilter) // Add Array size 1 (for Recipe IDs) addVarint(&buf, int32(len(recipeIDs1))) for _, recipeID := range recipeIDs1 { addString(&buf, recipeID) } // If action is 0 (init), add Array size 2 and its Recipe IDs if action == 0 { addVarint(&buf, int32(len(recipeIDs2))) for _, recipeID := range recipeIDs2 { addString(&buf, recipeID) } } // Send packet with ID 0x41 return player.sendPacket(0x41, &buf) } func (player *Player) sendRemoveEntities(entities []*Entity) error { if len(entities) > 0 { var buf bytes.Buffer addVarint(&buf, int32(len(entities))) for _, entity := range entities { addVarint(&buf, entity.ID) } return player.sendPacket(0x42, &buf) } return nil } // sendRemoveEntityEffect sends a packet to remove an entity's effect. func (player *Player) sendRemoveEntityEffect(entity *Entity, effectID int32) error { var buf bytes.Buffer // Add Entity ID addVarint(&buf, entity.ID) // Add Effect ID addVarint(&buf, effectID) // Send packet with ID 0x43 return player.sendPacket(0x43, &buf) } // sendResetScore sends a packet to reset a player's score. func (player *Player) sendResetScore(entityName string, objectiveName string) error { var buf bytes.Buffer // Add Entity Name addString(&buf, entityName) // Add Has an Objective Name flag addBool(&buf, len(objectiveName) > 0) // If there is an Objective Name, add it if len(objectiveName) > 0 { addString(&buf, objectiveName) } // Send packet with ID 0x44 return player.sendPacket(0x44, &buf) } // sendRemoveResourcePack sends a packet to remove a resource pack. func (player *Player) sendRemoveResourcePack(resourcePackUUID uuid.UUID) error { var buf bytes.Buffer // Add Has UUID flag addBool(&buf, resourcePackUUID != uuid.Nil) // If there's a specific resource pack, add its UUID if resourcePackUUID != uuid.Nil { addUUID(&buf, resourcePackUUID) } // Send a packet with ID 0x45 return player.sendPacket(0x45, &buf) } // Sends a packet to add a resource pack. func (player *Player) sendAddResourcePack(resourcePackUUID uuid.UUID, url string, hash string, forced bool, hasPrompt bool, promptMessage TextComponent) error { var buf bytes.Buffer // Add UUID addUUID(&buf, resourcePackUUID) // Add URL addString(&buf, url) // Add Hash (40-character hexadecimal string) addString(&buf, hash) // Add a Forced flag addBool(&buf, forced) // Add Has Prompt flag addBool(&buf, hasPrompt) // If there's a custom prompt, add it if hasPrompt { addTextComponent(&buf, promptMessage) } // Send packet with ID 0x46 return player.sendPacket(0x46, &buf) } // sendRespawn sends a respawn packet. func (player *Player) sendRespawn(keepAttributes bool, keepMetadata bool) error { var buf bytes.Buffer // Add Dimension Type addVarint(&buf, player.Dimension.ID) // Add Dimension Name addString(&buf, player.Dimension.Name) noise, err := world.Seed.MarshalBinary() if err != nil { return err } for i := 0; i < 8; i++ { addByte(&buf, noise[i]) } // Add Game Mode addByte(&buf, player.GameMode) // Add Previous Game Mode addByte(&buf, byte(player.PreviousGameMode)) // Add Is Debug flag addBool(&buf, world.Debug) // Add Is Flat flag addBool(&buf, world.Flat) // Add Has a Death Location flag addBool(&buf, player.DeathPosition != BlockPosition{}) // If the player has a death location, add the dimension and position if player.DeathPosition.X != 0 && player.DeathPosition.Y != 0 && player.DeathPosition.Z != 0 { addString(&buf, player.DeathDimension.Name) player.DeathPosition.add(&buf) } // Add Portal Cooldown addVarint(&buf, player.portalCooldown) var dataKept byte if keepAttributes { dataKept |= 0x01 } if keepMetadata { dataKept |= 0x02 } // Add Data Kept bitmask addByte(&buf, dataKept) // Send packet with ID 0x47 return player.sendPacket(0x47, &buf) } func (player *Player) sendHeadRotation(entity *Entity) error { var buf bytes.Buffer addVarint(&buf, entity.ID) addByte(&buf, entity.Rotation.Yaw) return player.sendPacket(0x48, &buf) } // sendUpdateSectionBlocks sends a packet to update section blocks. func (player *Player) sendUpdateSectionBlocks(chunkX, chunkY, chunkZ int, blocks []Block) error { var buf bytes.Buffer // Add Chunk Section Position (calculated from chunkX, chunkY, chunkZ) sectionPosition := ((chunkX & 0x3FFFFF) << 42) | (chunkZ&0x3FFFFF)<<20 | (chunkY & 0xFFFFF) addInt64(&buf, int64(sectionPosition)) // Add number of blocks addVarint(&buf, int32(len(blocks))) // Add each block's state and position for _, block := range blocks { blockData := int64(block.BlockStateID<<12) | int64(block.Position.X<<8|block.Position.Z<<4|int32(block.Position.Y)) addVarlong(&buf, blockData) } // Send packet with ID 0x49 return player.sendPacket(0x49, &buf) } // sendSelectAdvancementsTab sends a packet to select an advancement tab. func (player *Player) sendSelectAdvancementsTab() error { var buf bytes.Buffer // Add Has ID flag addBool(&buf, player.advancementTab != "") // If there's an ID, add it if player.advancementTab != "" { addString(&buf, player.advancementTab) } // Send a packet with ID 0x4A return player.sendPacket(0x4A, &buf) } // sendServerData sends a packet containing the server's MOTD and optional icon. func (player *Player) sendServerData() error { var buf bytes.Buffer // Add MOTD addTextComponent(&buf, TextComponent{ Text: "Hello there", Extra: nil, CleanText: "Hello there", }) // Add Has Icon flag addBool(&buf, len(iconData) > 0) // If there is an icon, add it if len(iconData) > 0 { addBytes(&buf, iconData) } // Send a packet with ID 0x4B return player.sendPacket(0x4B, &buf) } // sendSetActionBarText sends a packet to set the action bar text. func (player *Player) sendSetActionBarText(text TextComponent) error { var buf bytes.Buffer // Add Action Bar Text addTextComponent(&buf, text) // Send packet with ID 0x4C return player.sendPacket(0x4C, &buf) } // sendSetBorderCenter sends a packet to set the border center. func (player *Player) sendSetBorderCenter() error { var buf bytes.Buffer // Add X and Z coordinates addFloat64(&buf, player.Dimension.Border.X) addFloat64(&buf, player.Dimension.Border.Z) // Send a packet with ID 0x4D return player.sendPacket(0x4D, &buf) } // sendSetBorderLerpSize sends a packet to set the border lerp size. func (player *Player) sendSetBorderLerpSize() error { var buf bytes.Buffer // Add Old Diameter addFloat64(&buf, player.Dimension.Border.OldDiameter) // Add New Diameter addFloat64(&buf, player.Dimension.Border.NewDiameter) // Add Speed addVarlong(&buf, player.Dimension.Border.Speed) // Send packet with ID 0x4E return player.sendPacket(0x4E, &buf) } // sendSetBorderSize sends a packet to set the border size. func (player *Player) sendSetBorderSize() error { var buf bytes.Buffer // Add Diameter addFloat64(&buf, player.Dimension.Border.NewDiameter) // Send packet with ID 0x4F return player.sendPacket(0x4F, &buf) } // sendSetBorderWarningDelay sends a packet to set the border warning delay. func (player *Player) sendSetBorderWarningDelay() error { var buf bytes.Buffer // Add Warning Time (seconds) addVarint(&buf, player.Dimension.Border.WarningTime) // Send packet with ID 0x50 return player.sendPacket(0x50, &buf) } // sendSetBorderWarningDistance sends a packet to set the border warning distance. func (player *Player) sendSetBorderWarningDistance() error { var buf bytes.Buffer // Add Warning Distance (blocks) addVarint(&buf, player.Dimension.Border.WarningBlocks) // Send packet with ID 0x51 return player.sendPacket(0x51, &buf) } // sendSetCamera sends a packet to set the camera view to an entity. func (player *Player) sendSetCamera(entity *Entity) error { var buf bytes.Buffer // Add Camera Entity ID addVarint(&buf, entity.ID) // Send packet with ID 0x52 return player.sendPacket(0x52, &buf) } // sendSetHeldItem sends a packet to set the player's held item slot. func (player *Player) sendSetHeldItem() error { var buf bytes.Buffer // Add ItemStack addByte(&buf, player.inventory.activeSlot) // Send packet with ID 0x53 return player.sendPacket(0x53, &buf) } // sendSetCenterChunk sends a packet to set the player's center chunk. func (player *Player) sendSetCenterChunk() error { var buf bytes.Buffer chunkX, chunkZ := player.Position.toChunkPos() // Add Chunk X and Chunk Z addVarint(&buf, chunkX) addVarint(&buf, chunkZ) // Send packet with ID 0x54 return player.sendPacket(0x54, &buf) } // sendSetRenderDistance sends a packet to set the render distance. func (player *Player) sendSetRenderDistance() error { var buf bytes.Buffer // Add View Distance addVarint(&buf, player.Dimension.RenderDistance) // Send packet with ID 0x55 return player.sendPacket(0x55, &buf) } // sendSetDefaultSpawnPosition sends a packet to set the default spawn position. func (player *Player) sendSetDefaultSpawnPosition() error { var buf bytes.Buffer // Add Position player.Dimension.SpawnPosition.add(&buf) // Add Angle addFloat32(&buf, player.Dimension.SpawnAngle) // Send packet with ID 0x56 return player.sendPacket(0x56, &buf) } // sendDisplayObjective sends a packet to display an objective. func (player *Player) sendDisplayObjective(position byte, scoreName string) error { var buf bytes.Buffer // Add Position addByte(&buf, position) // Add Score Name addString(&buf, scoreName) // Send a packet with ID 0x57 return player.sendPacket(0x57, &buf) } // sendSetEntityMetadata sends a packet to update entity metadata. func (player *Player) sendSetEntityMetadata(entityID int32, metadata EntityMetadata) error { var buf bytes.Buffer // Add Entity ID addVarint(&buf, entityID) // Add EntityMetadata metadata.add(&buf) // Send packet with ID 0x58 return player.sendPacket(0x58, &buf) } // sendLinkEntities sends a packet to link two entities together. func (player *Player) sendLinkEntities(attached, holding *Entity) error { var buf bytes.Buffer // Add Attached Entity ID addVarint(&buf, attached.ID) // Add Holding Entity ID addVarint(&buf, holding.ID) // Send packet with ID 0x59 return player.sendPacket(0x59, &buf) } // sendSetEntityVelocity sends a packet to set an entity's velocity. func (player *Player) sendSetEntityVelocity(entity *Entity) error { var buf bytes.Buffer // Add Entity ID addVarint(&buf, entity.ID) // Add Velocities addInt16(&buf, int16(entity.Velocity.X)) addInt16(&buf, int16(entity.Velocity.Y)) addInt16(&buf, int16(entity.Velocity.Z)) // Send a packet with ID 0x5A return player.sendPacket(0x5A, &buf) } // sendSetEquipment sends a packet to update an entity's equipment. func (player *Player) sendSetEquipment(entity *Entity) error { var buf bytes.Buffer // Add Entity ID addVarint(&buf, entity.ID) for i := range 7 { slot := i if i == 6 { slot |= 0x80 } addByte(&buf, byte(slot)) player.inventory.slots[i].add(&buf) } // Send a packet with ID 0x5B return player.sendPacket(0x5B, &buf) } // Function to send experience update packet func (player *Player) sendSetExperience() error { var buf bytes.Buffer baseExperience := int32(7) experienceMultiplier := int32(3) level := int32(0) requiredExperience := int32(0) nextLevelExperience := baseExperience // Calculate level based on total experience for { // Calculate experience required to reach the next level requiredExperience += nextLevelExperience if player.XP < requiredExperience { break } // Increment level and calculate experience required for the next level level++ nextLevelExperience = baseExperience + (level*experienceMultiplier*(level+2))/2 } // Calculate experience bar as a percentage of the next level var experienceBar float32 if level > 0 { experienceBar = float32(player.XP-requiredExperience+nextLevelExperience) / float32(nextLevelExperience) // Calculate experience bar } else { experienceBar = float32(player.XP) / float32(baseExperience) // If the level is 0, baseExperience is used } // Add Experience Bar (percentage of next level) addFloat32(&buf, experienceBar) // Add Level addVarint(&buf, level) // Add Total Experience addVarint(&buf, player.XP) // Send a packet with ID 0x5C return player.sendPacket(0x5C, &buf) } // sendSetHealth sends a packet to update the player's health and food levels. func (player *Player) sendSetHealth() error { var buf bytes.Buffer // Add Health addFloat32(&buf, player.Entity.Health) // Add Food Level addVarint(&buf, player.FoodLevel) // Add Food Saturation addFloat32(&buf, player.FoodSaturation) // Send a packet with ID 0x5D return player.sendPacket(0x5D, &buf) } //TODO 0x5E update objectives func (player *Player) sendPassengers(entity *Entity) error { var buf bytes.Buffer addVarint(&buf, entity.ID) addVarint(&buf, int32(len(entity.Passengers))) for _, pass := range entity.Passengers { addVarint(&buf, pass.ID) } return player.sendPacket(0x5F, &buf) } //TODO 0X60 are teams //TODO 0x61 is scoreboard update func (player *Player) sendSimulationDistance() error { var buf bytes.Buffer addVarint(&buf, world.SimulationDistance) return player.sendPacket(0x62, &buf) } func (player *Player) sendSubtitleText(text TextComponent) error { var buf bytes.Buffer addTextComponent(&buf, text) return player.sendPacket(0x63, &buf) } func (player *Player) sendTime() error { var buf bytes.Buffer addInt64(&buf, player.Dimension.World.Age) addInt64(&buf, player.Dimension.World.Time) return player.sendPacket(0x64, &buf) } func (player *Player) sendTitleText(text TextComponent) error { var buf bytes.Buffer addTextComponent(&buf, text) return player.sendPacket(0x65, &buf) } func (player *Player) sendTitleTimes(fadeIn, stay, fadeout int32) error { var buf bytes.Buffer addInt32(&buf, fadeIn) addInt32(&buf, stay) addInt32(&buf, fadeout) return player.sendPacket(0x66, &buf) } func (player *Player) sendEntitySound(sound Sound, entity *Entity) error { var buf bytes.Buffer addVarint(&buf, sound.ID) if sound.ID == 0 { addString(&buf, sound.Name) addBool(&buf, sound.FixedRange) if sound.FixedRange { addFloat32(&buf, sound.Range) } } addVarint(&buf, sound.Category) addVarint(&buf, entity.ID) addFloat32(&buf, sound.Volume) addFloat32(&buf, sound.Pitch) addInt64(&buf, sound.Seed) return player.sendPacket(0x67, &buf) } // sendSoundEffect sends a sound effect packet to the player. func (player *Player) sendSoundEffect(sound Sound, position EntityPosition) error { var buf bytes.Buffer // Add Sound ID (Sound ID + 1; if 0, use Name) addVarint(&buf, sound.ID+1) // If Sound ID is 0, add the sound name, fixed range flag, and range if sound.ID == 0 { addString(&buf, sound.Name) addBool(&buf, sound.FixedRange) if sound.FixedRange { addFloat32(&buf, sound.Range) } } // Add Sound Category addVarint(&buf, sound.Category) // Add Effect Position X, Y, Z (multiplied by 8 to handle the fixed-point encoding) position.encodeFixedPoint(&buf) // Add Volume and Pitch addFloat32(&buf, sound.Volume) addFloat32(&buf, sound.Pitch) // Add Seed addInt64(&buf, sound.Seed) // Send a packet with ID 0x68 return player.sendPacket(0x68, &buf) } func (player *Player) restartConfiguration() error { var buf bytes.Buffer return player.sendPacket(0x69, &buf) } // sendStopSound sends a packet to stop sound effects for the player. func (player *Player) sendStopSound(source int32, soundName string) error { var buf bytes.Buffer var flags byte if source >= 0 { flags |= 0x01 } if soundName != "" { flags |= 0x02 } // Add Flags addByte(&buf, flags) // If flags indicate that the Source is present (bit 0x1), add the Source if flags&0x1 != 0 { addVarint(&buf, source) } // If flags indicate that the Sound Name is present (bit 0x2), add the Sound Name if flags&0x2 != 0 { addString(&buf, soundName) } // Send packet with ID 0x6A return player.sendPacket(0x6A, &buf) } //TODO 0x6B is store cookie func (player *Player) sendSystemChatMessage(content TextComponent, overlay bool) error { var buf bytes.Buffer addTextComponent(&buf, content) addBool(&buf, overlay) return player.sendPacket(0x6C, &buf) } func (player *Player) sendTabHeaderAndFooter(header, footer TextComponent) error { var buf bytes.Buffer addTextComponent(&buf, header) addTextComponent(&buf, footer) return player.sendPacket(0x6D, &buf) } // sendTagQueryResponse sends a response to a query for a block/entity NBT tag. func (player *Player) sendTagQueryResponse(transactionID int32, nbtData []byte) error { var buf bytes.Buffer // Add Transaction ID addVarint(&buf, transactionID) // Add NBT data (can be TAG_END if no NBT is present) buf.Write(nbtData) // Send packet with ID 0x6E return player.sendPacket(0x6E, &buf) } // sendPickupItem sends a packet when an entity picks up an item. func (player *Player) sendPickupItem(collectedEntityID, collectorEntityID, itemCount int32) error { var buf bytes.Buffer // Add Collected Entity ID addVarint(&buf, collectedEntityID) // Add Collector Entity ID addVarint(&buf, collectorEntityID) // Add Pickup Item Count addVarint(&buf, itemCount) // Send packet with ID 0x6F return player.sendPacket(0x6F, &buf) } // sendTeleportEntity sends a teleport entity packet. func (player *Player) sendTeleportEntity(entity *Entity) error { var buf bytes.Buffer // Add Entity ID addVarint(&buf, entity.ID) // Add Position (X, Y, Z) entity.Position.add(&buf) // Add Yaw and Pitch entity.Rotation.addBasic(&buf) // Add On a Ground flag addBool(&buf, entity.onGround()) // Send packet with ID 0x70 return player.sendPacket(0x70, &buf) } // sendSetTickingState sends a packet to adjust the client's ticking state. func (player *Player) sendSetTickingState() error { var buf bytes.Buffer // Add Tick Rate addFloat32(&buf, world.TickRate) // Add Is Frozen flag addBool(&buf, world.TickRate <= 0) // Send a packet with ID 0x71 return player.sendPacket(0x71, &buf) } // sendStepTick sends a packet to advance the client's processing by a specified number of ticks. func (player *Player) sendStepTick(tickSteps int32) error { var buf bytes.Buffer // Add Tick Steps addVarint(&buf, tickSteps) // Send a packet with ID 0x72 return player.sendPacket(0x72, &buf) } // sendTransferPlay sends a packet to notify the client to transfer to a new server. func (player *Player) sendTransferPlay(host string, port int32) error { var buf bytes.Buffer // Add Host (server hostname or IP) addString(&buf, host) // Add Port addVarint(&buf, port) // Send a packet with ID 0x73 return player.sendPacket(0x73, &buf) } //TODO 0x74 are advancements update // sendUpdateAttributes sends an attribute update packet for the given entity. func (player *Player) sendUpdateAttributes(entity *Entity) error { var buf bytes.Buffer // Add Entity ID addVarint(&buf, entity.ID) // Add Number Of Properties (Attributes) addVarint(&buf, int32(len(entity.Attributes))) // Iterate over all attributes and add them to the buffer for _, attr := range entity.Attributes { // Add EntityAttribute ID (VarInt Enum) addVarint(&buf, attr.ID) // Add EntityAttribute Value (Base Value) addFloat64(&buf, attr.Value) // Add Number Of Modifiers for this attribute addVarint(&buf, int32(len(attr.Modifiers))) // Add each modifier for _, mod := range attr.Modifiers { // Add Modifier ID (UUID or unique identifier) addUUID(&buf, mod.ID) // Add Modifier Amount addFloat64(&buf, mod.Amount) // Add Modifier Operation addByte(&buf, mod.Operation) } } // Send packet with ID 0x75 return player.sendPacket(0x75, &buf) } // sendEntityEffect sends an effect update packet to apply an effect on the given entity. func (player *Player) sendEntityEffect(entityEffect *EntityEffect) error { var buf bytes.Buffer // Add Entity ID (VarInt) addVarint(&buf, entityEffect.EntityID) // Add Effect ID (VarInt) addVarint(&buf, entityEffect.EffectID) // Add Amplifier (VarInt) addVarint(&buf, entityEffect.Amplifier) // Add Duration (VarInt, use -1 for infinite) addVarint(&buf, entityEffect.Duration) // Add Flags (Byte, bitfield) addByte(&buf, entityEffect.Flags) // Send a packet with ID 0x76 return player.sendPacket(0x76, &buf) } // sendUpdateRecipes sends the Update Recipes packet to the client func (player *Player) sendUpdateRecipes(packet *UpdateRecipesPacket) error { var buf bytes.Buffer // Add number of recipes (VarInt) addVarint(&buf, packet.NumRecipes) // Add each recipe entry for _, recipeEntry := range packet.Recipes { addVarint(&buf, recipeEntry.TypeID) // Add the recipe type (VarInt) recipeEntry.encode(&buf) // Add the recipe-specific data } // Send the packet (0x77) return player.sendPacket(0x77, &buf) } //0x78 is tags that are implemented cross-state func (player *Player) sendProjectilePower(entity *Entity) error { var buf bytes.Buffer addVarint(&buf, entity.ID) addFloat64(&buf, entity.ProjectilePower) return player.sendPacket(0x79, &buf) } //0x7A are report details that are implemented cross-state //0x7B are server links that are implemented cross-state