Backup
This commit is contained in:
645
structs.go
645
structs.go
@@ -5,9 +5,12 @@ import (
|
||||
"bytes"
|
||||
"crypto/cipher"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"github.com/google/uuid"
|
||||
"io"
|
||||
"math"
|
||||
"net"
|
||||
"reflect"
|
||||
"time"
|
||||
)
|
||||
|
||||
@@ -62,9 +65,15 @@ func (abilities *PlayerAbilites) export() (out byte) {
|
||||
return
|
||||
}
|
||||
|
||||
type Inventory struct {
|
||||
activeSlot uint8
|
||||
slots []ItemStack
|
||||
}
|
||||
|
||||
type Player struct {
|
||||
conn net.Conn
|
||||
|
||||
PermissionLevel uint8
|
||||
state int32
|
||||
version int32
|
||||
requestedAddress string
|
||||
@@ -82,8 +91,11 @@ type Player struct {
|
||||
Dimension *Dimension
|
||||
DeathDimension *Dimension
|
||||
DeathPosition BlockPosition
|
||||
CursorStack ItemStack
|
||||
|
||||
Abilites PlayerAbilites
|
||||
WindowID uint8
|
||||
Window *Window
|
||||
FlyingSpeed float32
|
||||
FOVModifier float32
|
||||
MessageIndex int32
|
||||
@@ -92,6 +104,7 @@ type Player struct {
|
||||
CombatEnd int64
|
||||
|
||||
Position EntityPosition
|
||||
Rotation EntityRotation
|
||||
|
||||
GameMode uint8
|
||||
PreviousGameMode int8
|
||||
@@ -109,6 +122,7 @@ type Player struct {
|
||||
keepAliveSentTimestamp int64
|
||||
keepAliveReceivedTimestamp int64
|
||||
|
||||
completionID int32
|
||||
pingID uint32
|
||||
pingSentTimestamp int64
|
||||
pingReceivedTimestamp int64
|
||||
@@ -116,7 +130,18 @@ type Player struct {
|
||||
|
||||
lastContainerStateID int32
|
||||
|
||||
name string
|
||||
teleportID int32
|
||||
waitingForTeleportConfirm bool
|
||||
portalCooldown int32
|
||||
|
||||
inventory Inventory
|
||||
|
||||
advancementTab string
|
||||
|
||||
XP int32
|
||||
FoodLevel int32
|
||||
FoodSaturation float32
|
||||
|
||||
uuid uuid.UUID
|
||||
}
|
||||
|
||||
@@ -135,9 +160,48 @@ func (player *Player) endCombat() {
|
||||
}
|
||||
}
|
||||
|
||||
func (player *Player) processChatCommand(command string) {
|
||||
for _, iterPlayer := range player.Dimension.World.Players {
|
||||
iterPlayer.sendSystemChatMessage(getTextComponent(fmt.Sprintf("%s ran %s", player.Entity.Name, command)), false)
|
||||
}
|
||||
}
|
||||
|
||||
func (player *Player) handleChatMessage(message string) {
|
||||
for _, iterPlayer := range player.Dimension.World.Players {
|
||||
iterPlayer.sendChatMessage(player, message)
|
||||
}
|
||||
}
|
||||
|
||||
func (player *Player) requestCommandSuggestions(id int32, text string) {
|
||||
player.completionID = id
|
||||
//TODO implement
|
||||
}
|
||||
|
||||
func (player *Player) IsInSurvivalMode() bool {
|
||||
return player.GameMode == 0
|
||||
}
|
||||
|
||||
func (player *Player) IsInCreativeMode() bool {
|
||||
return player.GameMode == 1
|
||||
}
|
||||
|
||||
func (player *Player) IsInAdventureMode() bool {
|
||||
return player.GameMode == 2
|
||||
}
|
||||
|
||||
func (player *Player) IsInSpectatorMode() bool {
|
||||
return player.GameMode == 3
|
||||
}
|
||||
|
||||
type Dimension struct {
|
||||
ID int32
|
||||
Name string
|
||||
ID int32
|
||||
Name string
|
||||
Chunks [][][]Chunk
|
||||
Border WorldBorder
|
||||
RenderDistance int32
|
||||
SpawnPosition EntityPosition
|
||||
SpawnAngle float32
|
||||
World *World
|
||||
}
|
||||
|
||||
type World struct {
|
||||
@@ -156,6 +220,9 @@ type World struct {
|
||||
Flat bool
|
||||
EnforceSecureChat bool
|
||||
|
||||
Age int64
|
||||
Time int64
|
||||
|
||||
Entities []Entity
|
||||
Players []Player
|
||||
RegistryDataThings []RegistryData
|
||||
@@ -165,6 +232,8 @@ type World struct {
|
||||
ReportDetails map[string]string
|
||||
ServerLinks []ServerLink
|
||||
|
||||
TickRate float32
|
||||
|
||||
Maps map[int32]Map
|
||||
}
|
||||
|
||||
@@ -180,6 +249,18 @@ type EntityPosition struct {
|
||||
Z float64
|
||||
}
|
||||
|
||||
func (entityPosition *EntityPosition) encodeFixedPoint(buf *bytes.Buffer) {
|
||||
addInt32(buf, int32(entityPosition.X*8))
|
||||
addInt32(buf, int32(entityPosition.Y*8))
|
||||
addInt32(buf, int32(entityPosition.Z*8))
|
||||
}
|
||||
|
||||
func (entityPosition *EntityPosition) toChunkPos() (chunkX, chunkZ int32) {
|
||||
chunkX = int32(int64(entityPosition.X) >> 4)
|
||||
chunkZ = int32(int64(entityPosition.Z) >> 4)
|
||||
return
|
||||
}
|
||||
|
||||
type FloatOffset struct {
|
||||
X float32
|
||||
Y float32
|
||||
@@ -209,6 +290,17 @@ type ExplosionSound struct {
|
||||
FixedRange float32
|
||||
}
|
||||
|
||||
type Sound struct {
|
||||
ID int32
|
||||
Name string
|
||||
FixedRange bool
|
||||
Range float32
|
||||
Category int32
|
||||
Volume float32
|
||||
Pitch float32
|
||||
Seed int64
|
||||
}
|
||||
|
||||
func (explosionSound *ExplosionSound) add(buf *bytes.Buffer) {
|
||||
addString(buf, explosionSound.Name)
|
||||
hasFixedRange := explosionSound.FixedRange > 0
|
||||
@@ -395,13 +487,27 @@ type Explosion struct {
|
||||
}
|
||||
|
||||
type Slot struct {
|
||||
Stack *ItemStack
|
||||
Number int16
|
||||
}
|
||||
|
||||
func (s Slot) IsEmpty() bool {
|
||||
return s.Stack.Count == 0
|
||||
}
|
||||
|
||||
type ItemStack struct {
|
||||
Count int32
|
||||
ItemID int32
|
||||
ComponentsToAdd map[int32]any
|
||||
ComponentsToRemove []int32
|
||||
MaxCount int32
|
||||
}
|
||||
|
||||
func (slot *Slot) add(buf *bytes.Buffer) {
|
||||
func (slot *ItemStack) IsEmpty() bool {
|
||||
return slot.Count == 0
|
||||
}
|
||||
|
||||
func (slot *ItemStack) add(buf *bytes.Buffer) {
|
||||
addVarint(buf, slot.Count)
|
||||
if slot.Count > 0 {
|
||||
addVarint(buf, slot.ItemID)
|
||||
@@ -417,7 +523,7 @@ func (slot *Slot) add(buf *bytes.Buffer) {
|
||||
}
|
||||
}
|
||||
|
||||
func (slot *Slot) addTrade(buf *bytes.Buffer) {
|
||||
func (slot *ItemStack) addTrade(buf *bytes.Buffer) {
|
||||
addVarint(buf, slot.Count)
|
||||
if slot.Count > 0 {
|
||||
addVarint(buf, slot.ItemID)
|
||||
@@ -432,6 +538,264 @@ func (slot *Slot) addTrade(buf *bytes.Buffer) {
|
||||
}
|
||||
}
|
||||
|
||||
func (slot *ItemStack) splitStack() ItemStack {
|
||||
if slot.Count > 1 {
|
||||
inNum := slot.Count
|
||||
outNum := slot.Count / 2
|
||||
inNum -= outNum
|
||||
slot.Count = inNum
|
||||
|
||||
out := *slot
|
||||
out.Count = outNum
|
||||
return out
|
||||
|
||||
}
|
||||
return ItemStack{}
|
||||
}
|
||||
|
||||
func (slot *ItemStack) takeOne() *ItemStack {
|
||||
if slot.Count > 1 {
|
||||
slot.Count -= 1
|
||||
out := *slot
|
||||
out.Count = 1
|
||||
return &out
|
||||
|
||||
}
|
||||
return &ItemStack{}
|
||||
}
|
||||
|
||||
func (slot *ItemStack) IsSameType(item *ItemStack) bool {
|
||||
return slot.ItemID == item.ItemID && reflect.DeepEqual(slot.ComponentsToAdd, item.ComponentsToAdd) && reflect.DeepEqual(slot.ComponentsToRemove, item.ComponentsToRemove)
|
||||
}
|
||||
|
||||
func (slot *ItemStack) Clone() ItemStack {
|
||||
return *slot
|
||||
}
|
||||
|
||||
func (slot *ItemStack) AddToStack(i *ItemStack) {
|
||||
if slot.Count == 0 {
|
||||
slot.Count = i.Count
|
||||
slot.ItemID = i.ItemID
|
||||
slot.ComponentsToAdd = i.ComponentsToAdd
|
||||
slot.ComponentsToRemove = i.ComponentsToRemove
|
||||
}
|
||||
}
|
||||
|
||||
func (slot *ItemStack) IsFull() bool {
|
||||
return slot.Count >= slot.MaxCount
|
||||
}
|
||||
|
||||
func (slot *ItemStack) TakeAll() *ItemStack {
|
||||
newSlot := *slot
|
||||
slot.Count = 0
|
||||
return &newSlot
|
||||
}
|
||||
|
||||
func (player *Player) handleClickContainer(packet ClickContainerPacket) error {
|
||||
if player.WindowID < 255 {
|
||||
return fmt.Errorf("invalid window ID")
|
||||
}
|
||||
|
||||
switch packet.Mode {
|
||||
case 0: // Normal click mode
|
||||
switch packet.Button {
|
||||
case 0: // Left click
|
||||
handleLeftClick(player.Window, packet.Slot, packet.CarriedItem)
|
||||
case 1: // Right click
|
||||
handleRightClick(player.Window, packet.Slot, packet.CarriedItem)
|
||||
default:
|
||||
return fmt.Errorf("unknown button: %d", packet.Button)
|
||||
}
|
||||
|
||||
case 1: // Shift-click mode
|
||||
handleShiftClick(player.Window, packet.Slot, packet.Button)
|
||||
|
||||
case 2: // Number key
|
||||
handleNumberKeyClick(player.Window, packet.Slot, packet.Button)
|
||||
|
||||
case 3: // Middle click (for creative mode)
|
||||
if player.IsInCreativeMode() {
|
||||
handleMiddleClick(player.Window, packet.Slot, player)
|
||||
}
|
||||
|
||||
case 4: // Drop key
|
||||
handleDropKey(player.Window, packet.Slot, packet.Button)
|
||||
|
||||
case 5: // Dragging items (painting mode)
|
||||
handleDraggingMode(player.Window, packet)
|
||||
|
||||
case 6: // Double click
|
||||
handleDoubleClick(player.Window, packet.Slot)
|
||||
|
||||
default:
|
||||
return fmt.Errorf("unknown mode: %d", packet.Mode)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func handleLeftClick(window *Window, slot int16, carriedItem ItemStack) {
|
||||
if carriedItem.IsEmpty() {
|
||||
// Pick up the stack from the slot
|
||||
window.Slots[slot] = Slot{} // Clear the slot
|
||||
} else {
|
||||
// Place the carried item into the slot
|
||||
window.setSlot(slot, &carriedItem)
|
||||
}
|
||||
}
|
||||
|
||||
func handleRightClick(window *Window, slot int16, carriedItem ItemStack) {
|
||||
slotData := window.GetSlot(slot)
|
||||
|
||||
if carriedItem.IsEmpty() {
|
||||
// Pick up half of the stack in the slot
|
||||
|
||||
window.Player.CursorStack = slotData.splitStack()
|
||||
window.setSlot(slot, slotData) // Update with remaining items
|
||||
} else {
|
||||
// Place one item from the carried stack into the slot
|
||||
if slotData.IsEmpty() {
|
||||
window.setSlot(slot, carriedItem.takeOne())
|
||||
} else if slotData.IsSameType(&carriedItem) {
|
||||
window.AddToSlot(slot, carriedItem.takeOne())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func handleShiftClick(window *Window, slot int16, button int8) {
|
||||
item := window.GetSlot(slot)
|
||||
|
||||
if item.IsEmpty() {
|
||||
return
|
||||
}
|
||||
|
||||
if button == 0 {
|
||||
// Move the item to the player's inventory or hotbar
|
||||
window.MoveItemToOtherInventory(item, slot)
|
||||
} else {
|
||||
// Handle shift-right-click if needed
|
||||
}
|
||||
}
|
||||
|
||||
func handleMiddleClick(window *Window, slot int16, player *Player) {
|
||||
if !player.IsInCreativeMode() {
|
||||
return // Middle click is only allowed in creative mode
|
||||
}
|
||||
|
||||
itemInSlot := window.GetSlot(slot)
|
||||
|
||||
if itemInSlot.IsEmpty() {
|
||||
return // No item in the slot to duplicate
|
||||
}
|
||||
|
||||
clonedItem := itemInSlot.Clone() // Clone the item
|
||||
|
||||
// Give the player the cloned item
|
||||
player.CursorStack = clonedItem
|
||||
}
|
||||
|
||||
func handleDropKey(window *Window, slot int16, button int8) {
|
||||
itemInSlot := window.GetSlot(slot)
|
||||
|
||||
if itemInSlot.IsEmpty() {
|
||||
return // No item to drop
|
||||
}
|
||||
|
||||
if button == 0 {
|
||||
// Drop the entire stack
|
||||
dropItem(itemInSlot, window.Player.Position)
|
||||
window.setSlot(slot, &ItemStack{}) // Remove the stack from the slot
|
||||
} else if button == 1 {
|
||||
// Drop a single item (Ctrl + Drop)
|
||||
singleItem := itemInSlot.takeOne()
|
||||
dropItem(singleItem, window.Player.Position)
|
||||
|
||||
if itemInSlot.IsEmpty() {
|
||||
window.setSlot(slot, &ItemStack{}) // Clear the slot if empty
|
||||
} else {
|
||||
window.setSlot(slot, itemInSlot) // Update the remaining stack
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func dropItem(item *ItemStack, position EntityPosition) {
|
||||
//TODO This function handles the actual item drop in the world
|
||||
// It could be implemented to spawn an entity that represents the dropped item
|
||||
}
|
||||
|
||||
func handleDoubleClick(window *Window, slot int16) {
|
||||
itemInSlot := window.GetSlot(slot)
|
||||
|
||||
if itemInSlot.IsEmpty() {
|
||||
return // Nothing to double-click on
|
||||
}
|
||||
|
||||
carriedItem := itemInSlot.Clone() // Pick up the clicked stack
|
||||
window.setSlot(slot, &ItemStack{}) // Clear the clicked slot
|
||||
|
||||
// Loop through the entire inventory and pick up items of the same type
|
||||
for i := range window.Slots {
|
||||
slotItem := window.GetSlot(int16(i))
|
||||
|
||||
if slotItem.IsSameType(&carriedItem) {
|
||||
carriedItem.AddToStack(slotItem.TakeAll())
|
||||
window.setSlot(int16(i), slotItem) // Update the slot with the remaining stack
|
||||
}
|
||||
|
||||
if carriedItem.IsFull() {
|
||||
break // Stop if the carried stack is full
|
||||
}
|
||||
}
|
||||
|
||||
// Set the player's carried item to the collected stack
|
||||
window.Player.CursorStack = carriedItem
|
||||
}
|
||||
|
||||
func handleNumberKeyClick(window *Window, slot int16, button int8) {
|
||||
hotbarIndex := button - 1 // The Button corresponds to number keys 1-9
|
||||
|
||||
if hotbarIndex < 0 || hotbarIndex >= 9 {
|
||||
return // Invalid hotbar index
|
||||
}
|
||||
|
||||
hotbarSlot := window.GetSlot(int16(hotbarIndex)) // Get the hotbar slot
|
||||
clickedSlot := window.GetSlot(slot) // Get the clicked slot
|
||||
|
||||
// Swap the hotbar slot with the clicked slot
|
||||
window.setSlot(int16(hotbarIndex), clickedSlot)
|
||||
window.setSlot(slot, hotbarSlot)
|
||||
}
|
||||
|
||||
func handleDraggingMode(window *Window, packet ClickContainerPacket) {
|
||||
switch packet.Button {
|
||||
case 0, 4, 8: // Start dragging
|
||||
startDragging(packet)
|
||||
|
||||
case 1, 5, 9: // Add slot to drag
|
||||
for _, changedSlot := range packet.ChangedSlots {
|
||||
window.setSlot(changedSlot.Number, changedSlot.Stack)
|
||||
}
|
||||
|
||||
case 2, 6, 10: // End dragging
|
||||
endDragging(packet)
|
||||
}
|
||||
}
|
||||
|
||||
func synchronizeContainer(player *Player, window *Window) {
|
||||
// Compare slot data with the server's state and resync if necessary
|
||||
player.sendContainerContent(window, &player.CursorStack)
|
||||
}
|
||||
|
||||
type ClickContainerPacket struct {
|
||||
WindowID uint8 // Unsigned Byte
|
||||
StateID int32 // Last received State ID
|
||||
Slot int16 // Clicked slot number
|
||||
Button int8 // Byte: the button used
|
||||
Mode int32 // Inventory operation mode
|
||||
ChangedSlots []Slot // Array of changed slots
|
||||
CarriedItem ItemStack // The item currently carried by the cursor
|
||||
}
|
||||
|
||||
// PalettedContainer represents a palette-based storage of entries.
|
||||
type PalettedContainer struct {
|
||||
BitsPerEntry uint8
|
||||
@@ -636,14 +1000,66 @@ func (bossBar BossBar) updateFlags(player *Player) error {
|
||||
}
|
||||
|
||||
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))
|
||||
// Get the 26-bit values for X and Z
|
||||
x := blockPosition.X
|
||||
z := blockPosition.Z
|
||||
y := blockPosition.Y
|
||||
|
||||
buf.Write(xBuf[0:26])
|
||||
buf.Write(zBuf[0:26])
|
||||
buf.Write(yBuf[0:12])
|
||||
// Pack the values into a byte slice
|
||||
var positionBuf [4]byte // Enough to hold both x and z (4 bytes total)
|
||||
positionBuf[0] = byte((x & 0x3FFFFFF) >> 18) // Upper 8 bits of x
|
||||
positionBuf[1] = byte((x & 0x3FFFF) >> 10) // Middle 8 bits of x
|
||||
positionBuf[2] = byte((x & 0x3FF) >> 2) // Lower 8 bits of x
|
||||
positionBuf[3] = byte(((x & 0x3) << 6) | ((z & 0x3FFFFFF) >> 20)) // Last 2 bits of x and upper 6 bits of z
|
||||
|
||||
// Write X and Z to the buffer
|
||||
buf.Write(positionBuf[:])
|
||||
|
||||
// Continue packing Z into the next part of the byte slice
|
||||
positionBuf[0] = byte((z & 0x3FFFFFF) >> 18) // Upper 8 bits of z
|
||||
positionBuf[1] = byte((z & 0x3FFFF) >> 10) // Middle 8 bits of z
|
||||
positionBuf[2] = byte((z & 0x3FF) >> 2) // Lower 8 bits of z
|
||||
positionBuf[3] = byte((z & 0x3) << 6) // Last 2 bits of z
|
||||
|
||||
// Write Z to the buffer
|
||||
buf.Write(positionBuf[:])
|
||||
|
||||
// Pack Y into the buffer (12-bit signed integer)
|
||||
yBuf := [2]byte{
|
||||
byte((y & 0xFFF) >> 4), // Upper 8 bits of y
|
||||
byte((y & 0xF) << 4), // Last 4 bits of y
|
||||
}
|
||||
|
||||
// Write Y to the buffer
|
||||
buf.Write(yBuf[:])
|
||||
}
|
||||
|
||||
func (blockPosition *BlockPosition) receivePosition(buf *bytes.Buffer) error {
|
||||
if buf.Len() < 10 { // 8 bytes for x and z, 2 bytes for y
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
|
||||
// Read the first 8 bytes for x and z
|
||||
positionBuf := make([]byte, 8)
|
||||
if _, err := buf.Read(positionBuf); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Extract X from the first 4 bytes
|
||||
blockPosition.X = int32(positionBuf[0]&0x3F)<<18 | int32(positionBuf[1])<<10 | int32(positionBuf[2])<<2 | int32(positionBuf[3]&0x3F)
|
||||
|
||||
// Extract Z from the next 4 bytes
|
||||
blockPosition.Z = int32(positionBuf[4]&0x3F)<<18 | int32(positionBuf[5])<<10 | int32(positionBuf[6])<<2 | int32(positionBuf[7]&0x3F)
|
||||
|
||||
// Read the next 2 bytes for y
|
||||
yBuf := make([]byte, 2)
|
||||
if _, err := buf.Read(yBuf); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
blockPosition.Y = int16(int32(yBuf[0])<<4 | (int32(yBuf[1]) >> 4))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (entityPosition *EntityPosition) add(buf *bytes.Buffer) {
|
||||
@@ -670,6 +1086,11 @@ func (entityRotation *EntityRotation) add(buf *bytes.Buffer) {
|
||||
addByte(buf, entityRotation.HeadYaw)
|
||||
}
|
||||
|
||||
func (entityRotation *EntityRotation) addBasic(buf *bytes.Buffer) {
|
||||
addByte(buf, entityRotation.Pitch)
|
||||
addByte(buf, entityRotation.Yaw)
|
||||
}
|
||||
|
||||
type Velocity struct {
|
||||
X float32
|
||||
Y float32
|
||||
@@ -682,10 +1103,31 @@ func (velocity *Velocity) add(buf *bytes.Buffer) {
|
||||
addFloat32(buf, velocity.Z)
|
||||
}
|
||||
|
||||
type Modifier struct {
|
||||
ID uuid.UUID // UUID or another unique identifier for the modifier
|
||||
Amount float64 // Modifier value (positive or negative)
|
||||
Operation byte // Operation type (0 = Add, 1 = Add Percentage, 2 = Multiply Percentage)
|
||||
}
|
||||
|
||||
type EntityAttribute struct {
|
||||
ID int32 // The ID of the attribute (e.g., generic.max_health = 16)
|
||||
Value float64 // The base value of the attribute
|
||||
Modifiers []Modifier // List of modifiers applied to the attribute
|
||||
}
|
||||
|
||||
type EntityEffect struct {
|
||||
EntityID int32 // The ID of the entity being affected
|
||||
EffectID int32 // ID of the effect (see effect table below)
|
||||
Amplifier int32 // The effect level (amplifier + 1 is displayed)
|
||||
Duration int32 // Duration in ticks (-1 for infinite)
|
||||
Flags byte // Bitfield for ambient, particles, icon, and blend
|
||||
}
|
||||
|
||||
type Entity struct {
|
||||
World *World
|
||||
ID int32
|
||||
UUID uuid.UUID
|
||||
Name string
|
||||
Type int32
|
||||
Position EntityPosition
|
||||
Rotation EntityRotation
|
||||
@@ -696,6 +1138,11 @@ type Entity struct {
|
||||
PortalCooldown int32
|
||||
Vehicle *Entity
|
||||
PlayersInformed []*Player
|
||||
Health float32
|
||||
Passengers []*Entity
|
||||
Attributes []EntityAttribute // List of attributes for the entity
|
||||
Effects []EntityEffect // List of effects applied to the entity
|
||||
ProjectilePower float64
|
||||
}
|
||||
|
||||
// Function to calculate the Euclidean distance between two points in 3D space
|
||||
@@ -763,6 +1210,128 @@ func (XPOrb *XPOrb) add(buf *bytes.Buffer) {
|
||||
addInt16(buf, XPOrb.Count)
|
||||
}
|
||||
|
||||
// UpdateRecipesPacket Base struct for the Update Recipes packet
|
||||
type UpdateRecipesPacket struct {
|
||||
NumRecipes int32 // Number of recipes
|
||||
Recipes []RecipeEntry // List of recipes
|
||||
}
|
||||
|
||||
// RecipeEntry Base struct for different types of recipes
|
||||
type RecipeEntry struct {
|
||||
TypeID int32 // The recipe type (see recipe types table)
|
||||
Data Recipe // Data for this recipe type
|
||||
}
|
||||
|
||||
// Recipe Different recipe types
|
||||
type Recipe interface{}
|
||||
|
||||
// ShapedCraftingRecipe Shaped Crafting Recipe
|
||||
type ShapedCraftingRecipe struct {
|
||||
Group string // Group tag for similar recipes
|
||||
Category int32 // Category: Building, Redstone, etc.
|
||||
Width, Height int32 // Dimensions of the crafting grid
|
||||
Ingredients []Ingredient // Ingredients for the recipe
|
||||
Result ItemStack // Resulting item
|
||||
ShowNotification bool // Whether to show a toast notification
|
||||
}
|
||||
|
||||
// ShapelessCraftingRecipe Shapeless Crafting Recipe
|
||||
type ShapelessCraftingRecipe struct {
|
||||
Group string // Group tag for similar recipes
|
||||
Category int32 // Category: Building, Redstone, etc.
|
||||
Ingredients []Ingredient // List of ingredients (unordered)
|
||||
Result ItemStack // Resulting item
|
||||
ShowNotification bool // Whether to show a toast notification
|
||||
}
|
||||
|
||||
// SmeltingRecipe Smelting, Blasting, Smoking, and Campfire Cooking Recipes
|
||||
type SmeltingRecipe struct {
|
||||
Group string // Group tag for similar recipes
|
||||
Category int32 // Category: Food, Blocks, Misc
|
||||
Ingredient Ingredient // Smelting ingredient
|
||||
Result ItemStack // Resulting item
|
||||
Experience float32 // XP reward
|
||||
CookingTime int32 // Time to cook in ticks
|
||||
}
|
||||
|
||||
type Ingredient struct {
|
||||
Count int32 // Number of possible items for this ingredient
|
||||
Items []ItemStack // List of possible items (slots) that can satisfy this ingredient
|
||||
}
|
||||
|
||||
func (recipe *RecipeEntry) encode(buf *bytes.Buffer) {
|
||||
switch recipe := recipe.Data.(type) {
|
||||
case ShapedCraftingRecipe:
|
||||
encodeShapedCrafting(buf, recipe)
|
||||
case ShapelessCraftingRecipe:
|
||||
encodeShapelessCrafting(buf, recipe)
|
||||
case SmeltingRecipe:
|
||||
encodeSmelting(buf, recipe)
|
||||
// Other recipe types can be handled similarly...
|
||||
}
|
||||
}
|
||||
|
||||
func (ingredient *Ingredient) add(buf *bytes.Buffer) {
|
||||
addVarint(buf, ingredient.Count) // Number of possible items
|
||||
for _, slot := range ingredient.Items {
|
||||
slot.add(buf)
|
||||
}
|
||||
}
|
||||
|
||||
func encodeShapedCrafting(buf *bytes.Buffer, recipe ShapedCraftingRecipe) {
|
||||
addString(buf, recipe.Group)
|
||||
addVarint(buf, recipe.Category)
|
||||
addVarint(buf, recipe.Width)
|
||||
addVarint(buf, recipe.Height)
|
||||
|
||||
// Add each ingredient
|
||||
for _, ingredient := range recipe.Ingredients {
|
||||
ingredient.add(buf)
|
||||
}
|
||||
|
||||
// Add result slot
|
||||
recipe.Result.add(buf)
|
||||
|
||||
// Add a show notification flag (boolean)
|
||||
addBool(buf, recipe.ShowNotification)
|
||||
}
|
||||
|
||||
func encodeShapelessCrafting(buf *bytes.Buffer, recipe ShapelessCraftingRecipe) {
|
||||
addString(buf, recipe.Group)
|
||||
addVarint(buf, recipe.Category)
|
||||
|
||||
// Add number of ingredients (VarInt)
|
||||
addVarint(buf, int32(len(recipe.Ingredients)))
|
||||
|
||||
// Add each ingredient
|
||||
for _, ingredient := range recipe.Ingredients {
|
||||
ingredient.add(buf)
|
||||
}
|
||||
|
||||
// Add result slot
|
||||
recipe.Result.add(buf)
|
||||
|
||||
// Add a show notification flag (boolean)
|
||||
addBool(buf, recipe.ShowNotification)
|
||||
}
|
||||
|
||||
func encodeSmelting(buf *bytes.Buffer, recipe SmeltingRecipe) {
|
||||
addString(buf, recipe.Group)
|
||||
addVarint(buf, recipe.Category)
|
||||
|
||||
// Add ingredient
|
||||
recipe.Ingredient.add(buf)
|
||||
|
||||
// Add result slot
|
||||
recipe.Result.add(buf)
|
||||
|
||||
// Add experience (float)
|
||||
addFloat32(buf, recipe.Experience)
|
||||
|
||||
// Add cooking time (VarInt)
|
||||
addVarint(buf, recipe.CookingTime)
|
||||
}
|
||||
|
||||
type MapIcon struct {
|
||||
Type int32
|
||||
X int8
|
||||
@@ -824,9 +1393,9 @@ func (Map *Map) add(buf *bytes.Buffer) {
|
||||
}
|
||||
|
||||
type VillageTrade struct {
|
||||
In1 Slot
|
||||
In2 *Slot
|
||||
Out Slot
|
||||
In1 ItemStack
|
||||
In2 *ItemStack
|
||||
Out ItemStack
|
||||
Disabled bool
|
||||
Uses int32
|
||||
MaxUses int32
|
||||
@@ -870,6 +1439,48 @@ func (VillagerEntity *VillagerEntity) addOffers(buf *bytes.Buffer, windowID int3
|
||||
addBool(buf, VillagerEntity.CanRestock)
|
||||
}
|
||||
|
||||
type Window struct {
|
||||
ID int32
|
||||
type WindowInteractible interface {
|
||||
click()
|
||||
}
|
||||
|
||||
type Window struct {
|
||||
ID uint8
|
||||
Slots []Slot
|
||||
Type int32
|
||||
Player *Player
|
||||
}
|
||||
|
||||
func (window *Window) setSlot(index int16, stack *ItemStack) {
|
||||
window.Slots[index] = Slot{
|
||||
Stack: stack,
|
||||
Number: index,
|
||||
}
|
||||
}
|
||||
|
||||
func (window *Window) GetSlot(slot int16) *ItemStack {
|
||||
return window.Slots[slot].Stack
|
||||
}
|
||||
|
||||
func (window *Window) AddToSlot(slotIndex int16, stack *ItemStack) {
|
||||
if window.Slots[slotIndex].Stack.Count == 0 {
|
||||
window.Slots[slotIndex].Stack = stack
|
||||
stack.Count = 0
|
||||
} else if window.Slots[slotIndex].Stack.IsSameType(stack) && !window.Slots[slotIndex].Stack.IsFull() {
|
||||
addedCount := stack.Count
|
||||
leftOverCount := int32(0)
|
||||
if addedCount > stack.MaxCount {
|
||||
stack.MaxCount = addedCount
|
||||
leftOverCount = stack.Count - addedCount
|
||||
}
|
||||
window.Slots[slotIndex].Stack.Count += addedCount
|
||||
stack.Count = leftOverCount
|
||||
}
|
||||
}
|
||||
|
||||
type EntityMetadata struct {
|
||||
//TODO IMPLEMENT
|
||||
}
|
||||
|
||||
func (metadata *EntityMetadata) add(buf *bytes.Buffer) {
|
||||
//TODO IMPLEMENT
|
||||
}
|
||||
|
Reference in New Issue
Block a user