Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implemented lecterns #651

Merged
merged 8 commits into from
Jul 4, 2023
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ require (
github.com/google/uuid v1.3.0
github.com/pelletier/go-toml v1.9.4
github.com/rogpeppe/go-internal v1.3.0
github.com/sandertv/gophertunnel v1.24.0
github.com/sandertv/gophertunnel v1.24.3
github.com/sirupsen/logrus v1.8.1
go.uber.org/atomic v1.9.0
golang.org/x/exp v0.0.0-20220713135740-79cabaa25d75
Expand Down
10 changes: 2 additions & 8 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -147,14 +147,8 @@ github.com/rogpeppe/go-internal v1.3.0 h1:RR9dF3JtopPvtkroDZuVD7qquD0bnHlKSqaQhg
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/sandertv/go-raknet v1.11.1 h1:0auvhHoZNyC/Z1l5xqniE3JE+w3MGO3n3JXEGHzdlRE=
github.com/sandertv/go-raknet v1.11.1/go.mod h1:Gx+WgZBMQ0V2UoouGoJ8Wj6CDrMBQ4SB2F/ggpl5/+Y=
github.com/sandertv/gophertunnel v1.22.3 h1:xVTC348fU0gr62tbWnr8iuvZggOBgsKEm98sotZYhb8=
github.com/sandertv/gophertunnel v1.22.3/go.mod h1:dMOw79FHxr2azEqiGH20AwdljisAN1kqwu5SjPBnZ5k=
github.com/sandertv/gophertunnel v1.22.4-0.20220806023520-a5dad81212b7 h1:HyHfgJckEgNnQAnfOHAb2wwbPlAvw30x94WK6+7mSwA=
github.com/sandertv/gophertunnel v1.22.4-0.20220806023520-a5dad81212b7/go.mod h1:dMOw79FHxr2azEqiGH20AwdljisAN1kqwu5SjPBnZ5k=
github.com/sandertv/gophertunnel v1.22.4-0.20220809005900-b9efb6b66d6d h1:uvxgta0NzWM+SQnihcEZ1WbXmTEQ9ns8VVk08XLe1eQ=
github.com/sandertv/gophertunnel v1.22.4-0.20220809005900-b9efb6b66d6d/go.mod h1:dMOw79FHxr2azEqiGH20AwdljisAN1kqwu5SjPBnZ5k=
github.com/sandertv/gophertunnel v1.24.0 h1:TPxUJtBHeh3hRNQciAoKFcWa6FB8iyB3FqN8+sx4dh4=
github.com/sandertv/gophertunnel v1.24.0/go.mod h1:dMOw79FHxr2azEqiGH20AwdljisAN1kqwu5SjPBnZ5k=
github.com/sandertv/gophertunnel v1.24.3 h1:8bhUmqDjYmQJu2uRo9Y/iVhuzpfOVCSUhJygDP70iyk=
github.com/sandertv/gophertunnel v1.24.3/go.mod h1:dMOw79FHxr2azEqiGH20AwdljisAN1kqwu5SjPBnZ5k=
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72 h1:qLC7fQah7D6K1B0ujays3HV9gkFtllcxhzImRR7ArPQ=
Expand Down
2 changes: 1 addition & 1 deletion server/block/composter.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ func (c Composter) Activate(pos cube.Pos, _ cube.Face, w *world.World, u item.Us
if !ok {
return false
}
ctx.CountSub = 1
ctx.SubtractFromCount(1)
w.AddParticle(pos.Vec3(), particle.BoneMeal{})
if rand.Float64() > compostable.CompostChance() {
w.PlaySound(pos.Vec3(), sound.ComposterFill{})
Expand Down
5 changes: 5 additions & 0 deletions server/block/hash.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 5 additions & 9 deletions server/block/jukebox.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ func (j Jukebox) BreakInfo() BreakInfo {
}
return newBreakInfo(0.8, alwaysHarvestable, axeEffective, simpleDrops(d...)).withBreakHandler(func(pos cube.Pos, w *world.World, u item.User) {
if _, hasDisc := j.Disc(); hasDisc {
w.PlaySound(pos.Vec3(), sound.MusicDiscEnd{})
w.PlaySound(pos.Vec3Centre(), sound.MusicDiscEnd{})
}
})
}
Expand All @@ -51,22 +51,21 @@ func (j Jukebox) Activate(pos cube.Pos, _ cube.Face, w *world.World, u item.User

j.Item = item.Stack{}
w.SetBlock(pos, j, nil)
w.PlaySound(pos.Vec3(), sound.MusicDiscEnd{})
w.PlaySound(pos.Vec3Centre(), sound.MusicDiscEnd{})
} else if held, _ := u.HeldItems(); !held.Empty() {
if m, ok := held.Item().(item.MusicDisc); ok {
j.Item = held

w.SetBlock(pos, j, nil)
w.PlaySound(pos.Vec3(), sound.MusicDiscEnd{})
ctx.CountSub = 1
w.PlaySound(pos.Vec3Centre(), sound.MusicDiscEnd{})
ctx.SubtractFromCount(1)

w.PlaySound(pos.Vec3(), sound.MusicDiscPlay{DiscType: m.DiscType})
w.PlaySound(pos.Vec3Centre(), sound.MusicDiscPlay{DiscType: m.DiscType})
if u, ok := u.(jukeboxUser); ok {
u.SendJukeboxPopup(fmt.Sprintf("Now playing: %v - %v", m.DiscType.Author(), m.DiscType.DisplayName()))
}
}
}

return true
}

Expand All @@ -77,7 +76,6 @@ func (j Jukebox) Disc() (sound.DiscType, bool) {
return m.DiscType, true
}
}

return sound.DiscType{}, false
}

Expand All @@ -93,11 +91,9 @@ func (j Jukebox) EncodeNBT() map[string]any {
// DecodeNBT ...
func (j Jukebox) DecodeNBT(data map[string]any) any {
s := nbtconv.MapItem(data, "RecordItem")

if _, ok := s.Item().(item.MusicDisc); ok {
j.Item = s
}

return j
}

Expand Down
163 changes: 163 additions & 0 deletions server/block/lectern.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
package block

import (
"fmt"
"github.com/df-mc/dragonfly/server/block/cube"
"github.com/df-mc/dragonfly/server/block/model"
"github.com/df-mc/dragonfly/server/internal/nbtconv"
"github.com/df-mc/dragonfly/server/item"
"github.com/df-mc/dragonfly/server/world"
"github.com/df-mc/dragonfly/server/world/sound"
"github.com/go-gl/mathgl/mgl64"
"time"
)

// Lectern is a librarian's job site block found in villages. It is used to hold books for multiple players to read in
// multiplayer.
// TODO: Redstone functionality.
type Lectern struct {
bass
sourceWaterDisplacer

// Facing represents the direction the Lectern is facing.
Facing cube.Direction
// Book is the book currently held by the Lectern.
Book item.Stack
// Page is the page the Lectern is currently on in the book.
Page int
}

// Model ...
func (Lectern) Model() world.BlockModel {
return model.Lectern{}
}

// FuelInfo ...
func (Lectern) FuelInfo() item.FuelInfo {
return newFuelInfo(time.Second * 15)
}

// SideClosed ...
func (Lectern) SideClosed(cube.Pos, cube.Pos, *world.World) bool {
return false
}

// BreakInfo ...
func (l Lectern) BreakInfo() BreakInfo {
d := []item.Stack{item.NewStack(Lectern{}, 1)}
if !l.Book.Empty() {
d = append(d, l.Book)
}
return newBreakInfo(2, alwaysHarvestable, axeEffective, simpleDrops(d...))
}

// UseOnBlock ...
func (l Lectern) UseOnBlock(pos cube.Pos, face cube.Face, _ mgl64.Vec3, w *world.World, user item.User, ctx *item.UseContext) (used bool) {
pos, _, used = firstReplaceable(w, pos, face, l)
if !used {
return false
}
l.Facing = user.Facing().Opposite()
place(w, pos, l, user, ctx)
return placed(ctx)
}

// readableBook represents a book that can be read through a lectern.
type readableBook interface {
// TotalPages returns the total number of pages in the book.
TotalPages() int
// Page returns a specific page from the book and true when the page exists. It will otherwise return an empty string
// and false.
Page(page int) (string, bool)
}

// Activate ...
func (l Lectern) Activate(pos cube.Pos, _ cube.Face, w *world.World, u item.User, ctx *item.UseContext) bool {
if !l.Book.Empty() {
// We can't put a book on the lectern if it's full.
return true
}
held, _ := u.HeldItems()
if _, ok := held.Item().(readableBook); ok {
l.Page = 0
l.Book = held

w.SetBlock(pos, l, nil)
w.PlaySound(pos.Vec3Centre(), sound.LecternBookPlace{})
ctx.SubtractFromCount(1)
}
return true
JustTalDevelops marked this conversation as resolved.
Show resolved Hide resolved
}

// Punch ...
func (l Lectern) Punch(pos cube.Pos, _ cube.Face, w *world.World, _ item.User) {
if l.Book.Empty() {
// We can't remove a book from the lectern if there isn't one.
return
}

dropItem(w, l.Book, pos.Side(cube.FaceUp).Vec3Middle())

l.Book = item.Stack{}
w.SetBlock(pos, l, nil)
w.PlaySound(pos.Vec3Centre(), sound.Attack{})
}

// TurnPage updates the page the lectern is currently on to the page given.
func (l Lectern) TurnPage(pos cube.Pos, w *world.World, page int) error {
if page == l.Page {
// We're already on the correct page, so ignore this packet.
JustTalDevelops marked this conversation as resolved.
Show resolved Hide resolved
return nil
}
if l.Book.Empty() {
return fmt.Errorf("lectern at %v is empty", pos)
}
if r, ok := l.Book.Item().(readableBook); ok && (page >= r.TotalPages() || page < 0) {
return fmt.Errorf("page number %d is out of bounds", page)
}
l.Page = page
w.SetBlock(pos, l, nil)
return nil
}

// EncodeNBT ...
func (l Lectern) EncodeNBT() map[string]any {
m := map[string]any{
"hasBook": boolByte(!l.Book.Empty()),
"page": int32(l.Page),
"id": "Lectern",
}
if r, ok := l.Book.Item().(readableBook); ok {
m["book"] = nbtconv.WriteItem(l.Book, true)
m["totalPages"] = int32(r.TotalPages())
}
return m
}

// DecodeNBT ...
func (l Lectern) DecodeNBT(m map[string]any) any {
l.Page = int(nbtconv.Map[int32](m, "page"))
l.Book = nbtconv.MapItem(m, "book")
return l
}

// EncodeItem ...
func (Lectern) EncodeItem() (name string, meta int16) {
return "minecraft:lectern", 0
}

// EncodeBlock ...
func (l Lectern) EncodeBlock() (string, map[string]any) {
return "minecraft:lectern", map[string]any{
"direction": int32(horizontalDirection(l.Facing)),
"powered_bit": uint8(0), // We don't support redstone, anyway.
}
}

// allLecterns ...
func allLecterns() (lecterns []world.Block) {
for _, d := range cube.Directions() {
lecterns = append(lecterns, Lectern{Facing: d})
}
return
}
19 changes: 19 additions & 0 deletions server/block/model/lectern.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package model

import (
"github.com/df-mc/dragonfly/server/block/cube"
"github.com/df-mc/dragonfly/server/world"
)

// Lectern is a model used by lecterns.
type Lectern struct{}

// BBox ...
func (Lectern) BBox(cube.Pos, *world.World) []cube.BBox {
return []cube.BBox{full.ExtendTowards(cube.FaceDown, 0.1)}
JustTalDevelops marked this conversation as resolved.
Show resolved Hide resolved
}

// FaceSolid ...
func (Lectern) FaceSolid(cube.Pos, cube.Face, *world.World) bool {
return false
}
2 changes: 2 additions & 0 deletions server/block/register.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ func init() {
registerAll(allLanterns())
registerAll(allLava())
registerAll(allLeaves())
registerAll(allLecterns())
registerAll(allLight())
registerAll(allLitPumpkins())
registerAll(allLogs())
Expand Down Expand Up @@ -264,6 +265,7 @@ func init() {
world.RegisterItem(Kelp{})
world.RegisterItem(Ladder{})
world.RegisterItem(Lapis{})
world.RegisterItem(Lectern{})
world.RegisterItem(LitPumpkin{})
world.RegisterItem(Loom{})
world.RegisterItem(MelonSeeds{})
Expand Down
2 changes: 1 addition & 1 deletion server/block/sea_pickle.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ func (s SeaPickle) UseOnBlock(pos cube.Pos, face cube.Face, _ mgl64.Vec3, w *wor

existing.AdditionalCount++
w.SetBlock(pos, existing, nil)
ctx.CountSub = 1
ctx.SubtractFromCount(1)
return true
}

Expand Down
2 changes: 1 addition & 1 deletion server/block/wood_door.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ func (d WoodDoor) UseOnBlock(pos cube.Pos, face cube.Face, _ mgl64.Vec3, w *worl
ctx.IgnoreBBox = true
place(w, pos, d, user, ctx)
place(w, pos.Side(cube.FaceUp), WoodDoor{Wood: d.Wood, Facing: d.Facing, Top: true, Right: d.Right}, user, ctx)
ctx.CountSub = 1
ctx.SubtractFromCount(1)
return placed(ctx)
}

Expand Down
11 changes: 6 additions & 5 deletions server/internal/nbtconv/mapread.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,11 +78,12 @@ func MapBlock(x map[string]any, k string) world.Block {
// to a world.Item.
func MapItem(x map[string]any, k string) item.Stack {
if m, ok := x[k].(map[string]any); ok {
s := readItemStack(m)
readDamage(m, &s, true)
readEnchantments(m, &s)
readDisplay(m, &s)
readDragonflyData(m, &s)
t := Map[map[string]any](m, "tag")
JustTalDevelops marked this conversation as resolved.
Show resolved Hide resolved
s := readItemStack(m, t)
readDamage(t, &s, true)
readEnchantments(t, &s)
readDisplay(t, &s)
readDragonflyData(t, &s)
return s
}
return item.Stack{}
Expand Down
21 changes: 12 additions & 9 deletions server/internal/nbtconv/read.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,19 @@ import (

// ReadItem decodes the data of an item into an item stack.
func ReadItem(data map[string]any, s *item.Stack) item.Stack {
disk := s == nil
disk, tag := s == nil, data
if disk {
a := readItemStack(data)
tag = Map[map[string]any](data, "tag")

a := readItemStack(data, tag)
s = &a
}
readDamage(data, s, disk)
readAnvilCost(data, s)
readDisplay(data, s)
readEnchantments(data, s)
readDragonflyData(data, s)

readAnvilCost(tag, s)
readDamage(tag, s, disk)
readDisplay(tag, s)
readDragonflyData(tag, s)
readEnchantments(tag, s)
return *s
}

Expand All @@ -31,7 +34,7 @@ func ReadBlock(m map[string]any) world.Block {
}

// readItemStack reads an item.Stack from the NBT in the map passed.
func readItemStack(m map[string]any) item.Stack {
func readItemStack(m, t map[string]any) item.Stack {
var it world.Item
if blockItem, ok := MapBlock(m, "Block").(world.Item); ok {
it = blockItem
Expand All @@ -43,7 +46,7 @@ func readItemStack(m map[string]any) item.Stack {
return item.Stack{}
}
if n, ok := it.(world.NBTer); ok {
it = n.DecodeNBT(m).(world.Item)
it = n.DecodeNBT(t).(world.Item)
}
return item.NewStack(it, int(Map[byte](m, "Count")))
}
Expand Down