This commit is contained in:
2024-09-18 19:58:25 +02:00
commit efd6d64d01
9 changed files with 2782 additions and 0 deletions

716
structs.go Normal file
View File

@@ -0,0 +1,716 @@
package main
import (
"bufio"
"bytes"
"crypto/cipher"
"encoding/binary"
"github.com/google/uuid"
"math"
"net"
)
type Version struct {
Name string `json:"name"`
Protocol int `json:"protocol"`
}
type PlayerSample struct {
Name string `json:"name"`
ID string `json:"id"`
}
type Players struct {
Max int32 `json:"max"`
Online int `json:"online"`
Sample []PlayerSample `json:"sample"`
}
type Description struct {
Text string `json:"text"`
}
type ServerStatus struct {
Version Version `json:"version"`
Players Players `json:"players,omitempty"`
Description Description `json:"description,omitempty"`
Favicon string `json:"favicon,omitempty"`
EnforcesSecureChat bool `json:"enforcesSecureChat"`
}
type Player struct {
conn net.Conn
state int32
version int32
requestedAddress string
requestedPort uint16
compressionThreshold int32
verifyToken []byte
decryptStream cipher.Stream
encryptStream cipher.Stream
reader *bufio.Reader
profile MojangProfile
transferred bool
resourcePackResponse map[uuid.UUID]int32
knownPacks []DatapackInfo
Entity *Entity
Dimension *Dimension
DeathDimension *Dimension
DeathPosition BlockPosition
Position EntityPosition
GameMode uint8
PreviousGameMode int8
locale string
viewDistance int8
chatMode int32
chatColors bool
skinParts uint8
isRightHanded bool
textFiltering bool
serverListing bool
keepAlivePayload int64
keepAliveSentTimestamp int64
keepAliveReceivedTimestamp int64
pingID uint32
pingSentTimestamp int64
pingReceivedTimestamp int64
statistics []StatisticPiece
lastContainerStateID int32
name string
uuid uuid.UUID
}
type Dimension struct {
ID int32
Name string
}
type World struct {
Difficulty uint8
DifficultyLocked bool
Hardcore bool
Dimensions []Dimension
ViewDistance int32
SimulationDistance int32
ReducedDebugInfo bool
EnableRespawnScreen bool
DoLimitedCrafting bool
Seed uuid.UUID
Debug bool
Flat bool
EnforceSecureChat bool
Entities []Entity
Players []Player
RegistryDataThings []RegistryData
FeatureFlags []string
UpdateTags []UpdateTag
DataPacks []DatapackInfo
ReportDetails map[string]string
ServerLinks []ServerLink
Maps map[int32]Map
}
type StatisticPiece struct {
CategoryID int32
StatisticID int32
Value int32
}
type EntityPosition struct {
X float64
Y float64
Z float64
}
type FloatOffset struct {
X float32
Y float32
Z float32
}
type BlockPosition struct {
X int32
Y int16
Z int32
}
type AffectedBlockRecord struct {
OffsetX int8
OffsetY int8
OffsetZ int8
}
func (affectedBlockRecord *AffectedBlockRecord) add(buf *bytes.Buffer) {
addByte(buf, byte(affectedBlockRecord.OffsetX))
addByte(buf, byte(affectedBlockRecord.OffsetY))
addByte(buf, byte(affectedBlockRecord.OffsetZ))
}
type ExplosionSound struct {
Name string
FixedRange float32
}
func (explosionSound *ExplosionSound) add(buf *bytes.Buffer) {
addString(buf, explosionSound.Name)
hasFixedRange := explosionSound.FixedRange > 0
addBool(buf, hasFixedRange)
if hasFixedRange {
addFloat32(buf, explosionSound.FixedRange)
}
}
type Particle struct {
ParticleID int32
BlockState int32 // Used by block-related particles
FromColor [3]float32 // Used for color particles (RGB)
ToColor [3]float32 // Used for dust_color_transition particles (RGB)
Scale float32 // Used for dust/dust_color_transition particles
PositionSourceType int32 // Used for vibration particles
BlockPosition BlockPosition // Used for vibration particles (minecraft:block)
EntityID int32 // Used for vibration particles (minecraft:entity)
EntityEyeHeight float32 // Used for vibration particles (minecraft:entity)
Ticks int32 // Used for vibration particles
Delay int32 // Used for shriek particles
Count int32
LongDistance bool
Position EntityPosition
Offset FloatOffset
MaxSpeed float32
}
type WorldBorder struct {
X float64
Z float64
OldDiameter float64
NewDiameter float64
Speed int64
PortalTeleportBoundary int32
WarningBlocks int32
WarningTime int32
}
// Compute yaw between two positions
func calculateYaw(attackerPos, playerPos EntityPosition) float32 {
deltaX := attackerPos.X - playerPos.X
deltaZ := attackerPos.Z - playerPos.Z
// atan2 returns the angle in radians, we convert it to degrees
yaw := math.Atan2(deltaZ, deltaX) * (180 / math.Pi)
// In Minecraft, yaw 0 points towards positive Z, so we adjust the yaw to this system
yaw = -yaw + 90
// Normalize yaw to be between 0 and 360 degrees
if yaw < 0 {
yaw += 360
}
return float32(yaw)
}
func encodeARGB(color [3]float32) int32 {
// Encodes RGB color values into an ARGB int (alpha assumed to be 255)
r := int32(color[0] * 255)
g := int32(color[1] * 255)
b := int32(color[2] * 255)
a := int32(255)
return (a << 24) | (r << 16) | (g << 8) | b
}
func clampScale(scale float32) float32 {
// Clamps the scale between 0.01 and 4.0
if scale < 0.01 {
return 0.01
}
if scale > 4.0 {
return 4.0
}
return scale
}
func (p *Particle) addPacket(buf *bytes.Buffer, count int32) {
addBool(buf, p.LongDistance)
p.Position.add(buf)
p.Offset.add(buf)
addFloat32(buf, p.MaxSpeed)
addInt32(buf, count)
addInt32(buf, p.ParticleID)
// Handle based on particle ID
switch p.ParticleID {
case 1, 2, 28, 105: // minecraft:block, minecraft:block_marker, falling_dust, dust_pillar
addVarint(buf, p.BlockState)
case 13: // minecraft:dust
addFloat32(buf, p.FromColor[0])
addFloat32(buf, p.FromColor[1])
addFloat32(buf, p.FromColor[2])
addFloat32(buf, clampScale(p.Scale))
case 14: // minecraft:dust_color_transition
addFloat32(buf, p.FromColor[0])
addFloat32(buf, p.FromColor[1])
addFloat32(buf, p.FromColor[2])
addFloat32(buf, p.ToColor[0])
addFloat32(buf, p.ToColor[1])
addFloat32(buf, p.ToColor[2])
addFloat32(buf, clampScale(p.Scale))
case 20: // minecraft:entity_effect
addInt32(buf, encodeARGB(p.FromColor))
case 45: // minecraft:vibration
addVarint(buf, p.PositionSourceType)
if p.PositionSourceType == 0 {
p.BlockPosition.add(buf)
} else if p.PositionSourceType == 1 {
addVarint(buf, p.EntityID)
addFloat32(buf, p.EntityEyeHeight)
}
addVarint(buf, p.Ticks)
case 99: // minecraft:shriek
addVarint(buf, p.Delay)
// Particles with no additional data
case 0, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 15, 16, 17, 18, 19, 21, 22, 23, 24, 25, 26, 27, 29, 30, 31, 32, 33, 34, 36, 37, 38, 39, 40, 41, 42, 43, 44, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 100, 101, 102, 103, 104, 106, 107, 108:
// No additional data required
}
}
func (p *Particle) addExplosion(buf *bytes.Buffer) {
addInt32(buf, p.ParticleID)
// Handle based on particle ID
switch p.ParticleID {
case 1, 2, 28, 105: // minecraft:block, minecraft:block_marker, falling_dust, dust_pillar
addVarint(buf, p.BlockState)
case 13: // minecraft:dust
addFloat32(buf, p.FromColor[0])
addFloat32(buf, p.FromColor[1])
addFloat32(buf, p.FromColor[2])
addFloat32(buf, clampScale(p.Scale))
case 14: // minecraft:dust_color_transition
addFloat32(buf, p.FromColor[0])
addFloat32(buf, p.FromColor[1])
addFloat32(buf, p.FromColor[2])
addFloat32(buf, p.ToColor[0])
addFloat32(buf, p.ToColor[1])
addFloat32(buf, p.ToColor[2])
addFloat32(buf, clampScale(p.Scale))
case 20: // minecraft:entity_effect
addInt32(buf, encodeARGB(p.FromColor))
case 45: // minecraft:vibration
addVarint(buf, p.PositionSourceType)
if p.PositionSourceType == 0 {
p.BlockPosition.add(buf)
} else if p.PositionSourceType == 1 {
addVarint(buf, p.EntityID)
addFloat32(buf, p.EntityEyeHeight)
}
addVarint(buf, p.Ticks)
case 99: // minecraft:shriek
addVarint(buf, p.Delay)
// Particles with no additional data
case 0, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 15, 16, 17, 18, 19, 21, 22, 23, 24, 25, 26, 27, 29, 30, 31, 32, 33, 34, 36, 37, 38, 39, 40, 41, 42, 43, 44, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 100, 101, 102, 103, 104, 106, 107, 108:
// No additional data required
}
}
type Explosion struct {
Position EntityPosition
Strength float32
AffectedBlockRecords []AffectedBlockRecord
PlayerMotion EntityPosition
BlockInteraction int32
SmallExplosionParticle Particle
LargeExplosionParticle Particle
ExplosionSound ExplosionSound
}
type Slot struct {
Count int32
ItemID int32
ComponentsToAdd map[int32]any
ComponentsToRemove []int32
}
func (slot *Slot) add(buf *bytes.Buffer) {
addVarint(buf, slot.Count)
if slot.Count > 0 {
addVarint(buf, slot.ItemID)
addVarint(buf, int32(len(slot.ComponentsToAdd)))
addVarint(buf, int32(len(slot.ComponentsToRemove)))
for componentID, component := range slot.ComponentsToAdd {
addVarint(buf, componentID)
component = component //TODO IMPLEMENT ALL THESE STUPID COMPONENTS
}
for _, componentID := range slot.ComponentsToRemove {
addVarint(buf, componentID)
}
}
}
// PalettedContainer represents a palette-based storage of entries.
type PalettedContainer struct {
BitsPerEntry uint8
Palette []int32
DataArray []uint64 // Packed data for blocks or biomes
}
// AddEntry adds an entry (block or biome) to the palette and updates the DataArray.
func (p *PalettedContainer) AddEntry(entry int32, index int) {
// Check if the entry already exists in the palette.
for i, val := range p.Palette {
if val == entry {
// Entry already exists, no need to addExplosion to palette.
p.setEntryAtIndex(i, index)
return
}
}
// Add the new entry to the palette.
p.Palette = append(p.Palette, entry)
// Use the index of the new entry in the palette and store it in the DataArray.
p.setEntryAtIndex(len(p.Palette)-1, index)
}
// setEntryAtIndex sets the palette index for the block/biome at the specified index in the DataArray.
func (p *PalettedContainer) setEntryAtIndex(paletteIndex int, index int) {
// Each entry is packed into the DataArray with the specified BitsPerEntry.
entriesPerLong := 64 / p.BitsPerEntry
longIndex := index / int(entriesPerLong)
bitIndex := (index % int(entriesPerLong)) * int(p.BitsPerEntry)
// Clear the bits at the specified location before setting the new value.
mask := uint64(math.MaxUint64>>(64-p.BitsPerEntry)) << bitIndex
p.DataArray[longIndex] &= ^mask
// Set the new value.
p.DataArray[longIndex] |= (uint64(paletteIndex) & (math.MaxUint64 >> (64 - p.BitsPerEntry))) << bitIndex
}
// add serializes the PalettedContainer to a buffer for network transmission.
func (p *PalettedContainer) add(buf *bytes.Buffer) error {
// Write BitsPerEntry
buf.WriteByte(p.BitsPerEntry)
// Write Palette
if len(p.Palette) > 0 {
// Write the length of the palette as a VarInt.
addVarint(buf, int32(len(p.Palette)))
// Write each palette entry as a VarInt.
for _, entry := range p.Palette {
addVarint(buf, entry)
}
} else {
// Write a palette length of 0 for a global palette.
addVarint(buf, 0)
}
// Write DataArray
// First, write the length of the DataArray (in terms of the number of longs).
addVarint(buf, int32(len(p.DataArray)))
// Write each uint64 in the DataArray as a little-endian long.
for _, data := range p.DataArray {
err := binary.Write(buf, binary.BigEndian, data)
if err != nil {
return err
}
}
return nil
}
// ChunkSection represents a 16x16x16 section of a chunk.
type ChunkSection struct {
BlockCount uint16 // Non-air block count
BlockStates PalettedContainer // Block palette and states
Biomes PalettedContainer // Biome palette and data
}
// add serializes the ChunkSection into the buffer.
func (cs *ChunkSection) add(buf *bytes.Buffer) error {
err := binary.Write(buf, binary.BigEndian, cs.BlockCount)
if err != nil {
return err
}
// Write BlockStates and Biomes.
err = cs.BlockStates.add(buf)
if err != nil {
return err
}
err = cs.Biomes.add(buf)
if err != nil {
return err
}
return nil
}
// Chunk represents a chunk column, containing multiple chunk sections.
type Chunk struct {
X int32 // Chunk X coordinate
Z int32 // Chunk Z coordinate
Sections []ChunkSection // Chunk sections (16x16x16)
}
// add serializes the Chunk into the buffer.
func (c *Chunk) add(buf *bytes.Buffer) error {
err := binary.Write(buf, binary.BigEndian, c.X)
if err != nil {
return err
}
err = binary.Write(buf, binary.BigEndian, c.Z)
if err != nil {
return err
}
// Write the number of sections as a VarInt.
addVarint(buf, int32(len(c.Sections)))
// Write each chunk section.
for _, section := range c.Sections {
err := section.add(buf)
if err != nil {
return err
}
}
return nil
}
type Block struct {
Position BlockPosition
BlockEntityType int32
BlockTypeID int32
BlockStateID int32
BlockEntityData map[string]any
DestroyStage uint8
}
type BossBar struct {
UUID uuid.UUID
Title TextComponent
Health float32
Color int32
Division int32
Flags uint8
}
func (bossBar BossBar) add(player *Player) error {
var buf bytes.Buffer
addUUID(&buf, bossBar.UUID)
addVarint(&buf, 0)
addTextComponent(&buf, bossBar.Title)
addFloat32(&buf, bossBar.Health)
addVarint(&buf, bossBar.Color)
addVarint(&buf, bossBar.Division)
addByte(&buf, bossBar.Flags)
return player.sendPacket(0x0A, &buf)
}
func (bossBar BossBar) remove(player *Player) error {
var buf bytes.Buffer
addUUID(&buf, bossBar.UUID)
addVarint(&buf, 1)
return player.sendPacket(0x0A, &buf)
}
func (bossBar BossBar) updateHealth(player *Player) error {
var buf bytes.Buffer
addUUID(&buf, bossBar.UUID)
addVarint(&buf, 2)
addFloat32(&buf, bossBar.Health)
return player.sendPacket(0x0A, &buf)
}
func (bossBar BossBar) updateTitle(player *Player) error {
var buf bytes.Buffer
addUUID(&buf, bossBar.UUID)
addVarint(&buf, 3)
addTextComponent(&buf, bossBar.Title)
return player.sendPacket(0x0A, &buf)
}
func (bossBar BossBar) updateStyle(player *Player) error {
var buf bytes.Buffer
addUUID(&buf, bossBar.UUID)
addVarint(&buf, 4)
addVarint(&buf, bossBar.Color)
addVarint(&buf, bossBar.Division)
return player.sendPacket(0x0A, &buf)
}
func (bossBar BossBar) updateFlags(player *Player) error {
var buf bytes.Buffer
addUUID(&buf, bossBar.UUID)
addVarint(&buf, 5)
addByte(&buf, bossBar.Flags)
return player.sendPacket(0x0A, &buf)
}
func (blockPosition *BlockPosition) add(buf *bytes.Buffer) {
var xBuf, yBuf, zBuf []byte
binary.BigEndian.PutUint32(xBuf, uint32(blockPosition.X))
binary.BigEndian.PutUint16(yBuf, uint16(blockPosition.Y))
binary.BigEndian.PutUint32(zBuf, uint32(blockPosition.Z))
buf.Write(xBuf[0:26])
buf.Write(zBuf[0:26])
buf.Write(yBuf[0:12])
}
func (entityPosition *EntityPosition) add(buf *bytes.Buffer) {
addFloat64(buf, entityPosition.X)
addFloat64(buf, entityPosition.Y)
addFloat64(buf, entityPosition.Z)
}
func (floatOffset *FloatOffset) add(buf *bytes.Buffer) {
addFloat32(buf, floatOffset.X)
addFloat32(buf, floatOffset.Y)
addFloat32(buf, floatOffset.Z)
}
type EntityRotation struct {
Pitch uint8
Yaw uint8
HeadYaw uint8
}
func (entityRotation *EntityRotation) add(buf *bytes.Buffer) {
addByte(buf, entityRotation.Pitch)
addByte(buf, entityRotation.Yaw)
addByte(buf, entityRotation.HeadYaw)
}
type Velocity struct {
X float32
Y float32
Z float32
}
func (velocity *Velocity) add(buf *bytes.Buffer) {
addFloat32(buf, velocity.X)
addFloat32(buf, velocity.Y)
addFloat32(buf, velocity.Z)
}
type Entity struct {
ID int32
UUID uuid.UUID
Type int32
Position EntityPosition
Rotation EntityRotation
Data int32
Velocity Velocity
Animation uint8
status int8
PortalCooldown int32
}
func (entity *Entity) add(buf *bytes.Buffer) {
addVarint(buf, entity.ID)
addUUID(buf, entity.UUID)
addVarint(buf, entity.Type)
entity.Position.add(buf)
entity.Rotation.add(buf)
addVarint(buf, entity.Data)
entity.Velocity.add(buf)
}
type XPOrb struct {
ID int32
Position EntityPosition
Count int16
}
func (XPOrb *XPOrb) add(buf *bytes.Buffer) {
addVarint(buf, XPOrb.ID)
XPOrb.Position.add(buf)
addInt16(buf, XPOrb.Count)
}
type MapIcon struct {
Type int32
X int8
Z int8
Direction int8
DisplayName *TextComponent
}
func (MapIcon *MapIcon) add(buf *bytes.Buffer) {
addVarint(buf, MapIcon.Type)
addByte(buf, byte(MapIcon.X))
addByte(buf, byte(MapIcon.Z))
addByte(buf, byte(MapIcon.Direction))
hasTextComponent := MapIcon.DisplayName != nil
addBool(buf, hasTextComponent)
if hasTextComponent {
addTextComponent(buf, *MapIcon.DisplayName)
}
}
type Map struct {
ID int32
Scale int8
Locked bool
Icons []MapIcon
MapUpdateDataW uint8
MapUpdateDataH uint8
MapUpdateDataX uint8
MapUpdateDataZ uint8
MapUpdateData []uint8
}
func (Map *Map) add(buf *bytes.Buffer) {
addVarint(buf, Map.ID)
addByte(buf, byte(Map.Scale))
addBool(buf, Map.Locked)
iconCount := int32(len(Map.Icons))
addBool(buf, iconCount > 0)
if iconCount > 0 {
addVarint(buf, iconCount)
for _, mapIcon := range Map.Icons {
mapIcon.add(buf)
}
}
addByte(buf, Map.MapUpdateDataH)
if Map.MapUpdateDataH > 0 {
addByte(buf, Map.MapUpdateDataW)
addByte(buf, Map.MapUpdateDataX)
addByte(buf, Map.MapUpdateDataZ)
addVarint(buf, int32(len(Map.MapUpdateData)))
for _, mapUpdatePiece := range Map.MapUpdateData {
addByte(buf, mapUpdatePiece)
}
}
}