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 Books and Quills + Written Book #608

Merged
merged 35 commits into from
Aug 20, 2022
Merged
Show file tree
Hide file tree
Changes from 28 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
6ea616f
Implemented Books and Quills + Written Book
abimek Jul 28, 2022
4bc9bb0
fixing checks
abimek Jul 28, 2022
94a7650
Reordered register.go
abimek Jul 28, 2022
3678996
Documentation Fixes + Code Fixes
abimek Jul 28, 2022
ab0028c
Removal of Unnecessary Glinted Field
abimek Jul 28, 2022
81f3efe
Doc update
abimek Jul 28, 2022
3e9c4d2
removed excessive encoding field
abimek Jul 28, 2022
c71e835
Doc Changes + WrittenBookGeneration
abimek Jul 28, 2022
f4eca7b
Updates
abimek Aug 2, 2022
b36f5f8
Merge branch 'master' into feature/books
abimek Aug 2, 2022
4ea8a7a
Missing Docs
abimek Aug 2, 2022
742a00e
booo wiki
abimek Aug 2, 2022
0f8a787
Update server/item/written_book.go
JustTalDevelops Aug 2, 2022
89d78db
Merge branch 'master' into feature/books
JustTalDevelops Aug 3, 2022
f1e9f3f
server/item: Various improvements to Book and Quills.
JustTalDevelops Aug 3, 2022
32d624a
Merge branch 'master' into feature/books
JustTalDevelops Aug 7, 2022
a4ca352
item/written_book.go: Fix DecodeNBT on pages.
JustTalDevelops Aug 8, 2022
40acb60
Books and Quills are Done Now :D
abimek Aug 8, 2022
0681078
Validate Page Number within SetPage
abimek Aug 8, 2022
a671801
Merge branch 'master' into feature/books
DaPigGuy Aug 10, 2022
db551de
Update server/item/book_and_quill.go
abimek Aug 11, 2022
bef1e46
Update server/item/written_book.go
abimek Aug 11, 2022
8da9b01
Update server/session/handler_book_edit.go
abimek Aug 11, 2022
7a37c88
fix formatting
DaPigGuy Aug 11, 2022
ac21d40
Punctuation fixes.
abimek Aug 12, 2022
2cc3f04
Removes of ValidPage method for Boolean flag within Page() and use of…
abimek Aug 12, 2022
01b656a
Improved the Page method as well as the documentation.
abimek Aug 12, 2022
9afbd64
Moving Documentation for Consistency
abimek Aug 12, 2022
ea5c2bd
used duplicateStack() in order to save lore information and other ite…
abimek Aug 12, 2022
09c5122
Creation of InsertPage and DeletePage methods.
abimek Aug 12, 2022
738b9c4
Updated struct receiver variable name w -> b.
abimek Aug 12, 2022
d448ffa
Merge branch 'master' into feature/books
JustTalDevelops Aug 19, 2022
55bf763
item/book_and_quill.go: cleanup decode
DaPigGuy Aug 20, 2022
49eace2
item/written_book.go: cleanup decode
DaPigGuy Aug 20, 2022
2d6e3f3
session/handler_book_edit.go: duplicate item stack
DaPigGuy Aug 20, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 90 additions & 0 deletions server/item/book_and_quill.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package item

// BookAndQuill is an item used to write WrittenBook(s).
type BookAndQuill struct {
// Pages represents the pages within the book.
Pages []string
}

// MaxCount always returns 1.
func (w BookAndQuill) MaxCount() int {
return 1
}

// Page returns a specific page from the book and true when the page exists. It will otherwise return an empty string
// and false.
func (w BookAndQuill) Page(page int) (string, bool) {
if page < 0 || len(w.Pages) <= page {
return "", false
}
return w.Pages[page], true
}

// SetPage writes a page to the book, if the page doesn't exist it will be created. It will panic if the
// text is longer then 256 characters. It will return a new book representing this data.
func (w BookAndQuill) SetPage(page int, text string) BookAndQuill {
if page < 0 || page >= 50 {
panic("invalid page number")
}
if len(text) > 256 {
panic("text longer then 256 bytes")
}
if _, ok := w.Page(page); !ok {
pages := make([]string, page+1)
copy(pages, w.Pages)
w.Pages = pages
}
w.Pages[page] = text
return w
}

// SwapPages swaps two different pages, it will panic if the largest of the two numbers doesn't exist. It will
// return the newly updated pages.
func (w BookAndQuill) SwapPages(pageOne, pageTwo int) BookAndQuill {
if pageOne < 0 || pageTwo < 0 {
panic("negative page number")
}
if _, ok := w.Page(max(pageOne, pageTwo)); !ok {
panic("invalid page number")
}
temp := w.Pages[pageOne]
w.Pages[pageOne] = w.Pages[pageTwo]
w.Pages[pageTwo] = temp
return w
}

// DecodeNBT ...
func (w BookAndQuill) DecodeNBT(data map[string]any) any {
if pages, ok := data["pages"].([]any); ok {
for _, page := range pages {
if pageData, ok := page.(map[string]any); ok {
if text, ok := pageData["text"].(string); ok {
w.Pages = append(w.Pages, text)
}
}
}
}
return w
}

// EncodeNBT ...
func (w BookAndQuill) EncodeNBT() map[string]any {
pages := make([]any, 0, len(w.Pages))
for _, page := range w.Pages {
pages = append(pages, map[string]any{"text": page})
}
return map[string]any{"pages": pages}
}

// EncodeItem ...
func (w BookAndQuill) EncodeItem() (name string, meta int16) {
return "minecraft:writable_book", 0
}

// max ...
func max(a, b int) int {
if a > b {
return a
}
return b
}
2 changes: 2 additions & 0 deletions server/item/register.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ func init() {
world.RegisterItem(BlazeRod{})
world.RegisterItem(BoneMeal{})
world.RegisterItem(Bone{})
world.RegisterItem(BookAndQuill{})
world.RegisterItem(Book{})
world.RegisterItem(BottleOfEnchanting{})
world.RegisterItem(Bowl{})
Expand Down Expand Up @@ -118,6 +119,7 @@ func init() {
world.RegisterItem(TurtleShell{})
world.RegisterItem(WarpedFungusOnAStick{})
world.RegisterItem(Wheat{})
world.RegisterItem(WrittenBook{})
for _, t := range ArmourTiers() {
world.RegisterItem(Helmet{Tier: t})
world.RegisterItem(Chestplate{Tier: t})
Expand Down
75 changes: 75 additions & 0 deletions server/item/written_book.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package item

// WrittenBook is the item created after a book and quill is signed. It appears the same as a regular book, but
// without the quill, and has an enchanted-looking glint.
type WrittenBook struct {
// Title is the title of the book.
Title string
// Author is the author of the book.
Author string
// Generation is the copy tier of the book. 0 = original, 1 = copy of original,
// 2 = copy of copy.
Generation WrittenBookGeneration
// Pages represents the pages within the book.
Pages []string
}

// MaxCount always returns 1.
func (w WrittenBook) MaxCount() int {
return 1
}

// Page returns a specific page from the book and true when the page exists. It will otherwise return an empty string
// and false.
func (w WrittenBook) Page(page int) (string, bool) {
if page < 0 || len(w.Pages) <= page {
return "", false
}
return w.Pages[page], true
}

// DecodeNBT ...
func (w WrittenBook) DecodeNBT(data map[string]any) any {
if pages, ok := data["pages"].([]any); ok {
w.Pages = make([]string, len(pages))
for i, page := range pages {
w.Pages[i] = page.(map[string]any)["text"].(string)
}
}
if v, ok := data["title"].(string); ok {
w.Title = v
}
DaPigGuy marked this conversation as resolved.
Show resolved Hide resolved
if v, ok := data["author"].(string); ok {
w.Author = v
}
if v, ok := data["generation"].(uint8); ok {
switch v {
case 0:
w.Generation = OriginalGeneration()
case 1:
w.Generation = CopyGeneration()
case 2:
w.Generation = CopyOfCopyGeneration()
}
}
return w
}

// EncodeNBT ...
func (w WrittenBook) EncodeNBT() map[string]any {
pages := make([]any, 0, len(w.Pages))
for _, page := range w.Pages {
pages = append(pages, map[string]any{"text": page})
}
return map[string]any{
"pages": pages,
"author": w.Author,
"title": w.Title,
"generation": w.Generation.Uint8(),
}
}

// EncodeItem ...
func (w WrittenBook) EncodeItem() (name string, meta int16) {
return "minecraft:written_book", 0
}
41 changes: 41 additions & 0 deletions server/item/written_book_generation.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package item

// WrittenBookGeneration represents a WrittenBook generation.
type WrittenBookGeneration struct {
generation
}

type generation uint8

// OriginalGeneration is the original WrittenBook.
func OriginalGeneration() WrittenBookGeneration {
return WrittenBookGeneration{0}
}

// CopyGeneration is a copy of the original WrittenBook.
func CopyGeneration() WrittenBookGeneration {
return WrittenBookGeneration{1}
}

// CopyOfCopyGeneration is a copy of a copy of the original WrittenBook.
func CopyOfCopyGeneration() WrittenBookGeneration {
return WrittenBookGeneration{2}
}

// Uint8 returns the generation as a uint8.
func (g generation) Uint8() uint8 {
return uint8(g)
}

// String ...
func (g generation) String() string {
switch g {
case 0:
return "original"
case 1:
return "copy of original"
case 2:
return "copy of copy"
}
panic("unknown written book generation")
}
76 changes: 76 additions & 0 deletions server/session/handler_book_edit.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package session

import (
"fmt"
"github.com/df-mc/dragonfly/server/item"
"github.com/sandertv/gophertunnel/minecraft/protocol/packet"
"golang.org/x/exp/slices"
)

// BookEditHandler handles the BookEdit packet.
type BookEditHandler struct{}
abimek marked this conversation as resolved.
Show resolved Hide resolved

// Handle ...
func (b BookEditHandler) Handle(p packet.Packet, s *Session) error {
JustTalDevelops marked this conversation as resolved.
Show resolved Hide resolved
abimek marked this conversation as resolved.
Show resolved Hide resolved
pk := p.(*packet.BookEdit)

it, err := s.inv.Item(int(pk.InventorySlot))
if err != nil {
return fmt.Errorf("invalid inventory slot index %v", pk.InventorySlot)
}
book, ok := it.Item().(item.BookAndQuill)
if !ok {
return fmt.Errorf("inventory slot %v does not contain a writable book", pk.InventorySlot)
}

page := int(pk.PageNumber)
if page >= 50 || page < 0 {
return fmt.Errorf("page number %v is out of bounds", pk.PageNumber)
}
if len(pk.Text) > 256 {
return fmt.Errorf("text can not be longer than 256 bytes")
}
TwistedAsylumMC marked this conversation as resolved.
Show resolved Hide resolved

slot := int(pk.InventorySlot)
switch pk.ActionType {
case packet.BookActionReplacePage:
book = book.SetPage(page, pk.Text)
case packet.BookActionAddPage:
if len(book.Pages) >= 50 {
return fmt.Errorf("unable to add page beyond 50")
}
if page >= len(book.Pages) && page <= len(book.Pages)+2 {
DaPigGuy marked this conversation as resolved.
Show resolved Hide resolved
book = book.SetPage(page, "")
break
}
if _, ok := book.Page(page); !ok {
return fmt.Errorf("unable to insert page at %v", pk.PageNumber)
}
book = item.BookAndQuill{Pages: slices.Insert(book.Pages, page, pk.Text)}
abimek marked this conversation as resolved.
Show resolved Hide resolved
case packet.BookActionDeletePage:
if _, ok := book.Page(page); !ok {
// We break here instead of returning an error because the client can be a page or two ahead in the UI then
// the actual pages representation server side. The client still sends the deletion indexes.
DaPigGuy marked this conversation as resolved.
Show resolved Hide resolved
break
}
book = item.BookAndQuill{Pages: slices.Delete(book.Pages, page, page+1)}
abimek marked this conversation as resolved.
Show resolved Hide resolved
case packet.BookActionSwapPages:
if pk.SecondaryPageNumber >= 50 {
return fmt.Errorf("page number out of bounds")
}
_, ok := book.Page(page)
_, ok2 := book.Page(int(pk.SecondaryPageNumber))
if !ok || !ok2 {
// We break here instead of returning an error because the client can try to swap pages that don't exist.
// This happens as a result of the client being a page or two ahead in the UI then the actual pages
// representation server side. The client still sends the swap indexes.
DaPigGuy marked this conversation as resolved.
Show resolved Hide resolved
break
abimek marked this conversation as resolved.
Show resolved Hide resolved
}
book = book.SwapPages(page, int(pk.SecondaryPageNumber))
case packet.BookActionSign:
_ = s.inv.SetItem(slot, item.NewStack(item.WrittenBook{Title: pk.Title, Author: pk.Author, Pages: book.Pages, Generation: item.OriginalGeneration()}, 1))
JustTalDevelops marked this conversation as resolved.
Show resolved Hide resolved
DaPigGuy marked this conversation as resolved.
Show resolved Hide resolved
return nil
}
_ = s.inv.SetItem(slot, item.NewStack(book, 1))
abimek marked this conversation as resolved.
Show resolved Hide resolved
return nil
}
1 change: 1 addition & 0 deletions server/session/session.go
Original file line number Diff line number Diff line change
Expand Up @@ -434,6 +434,7 @@ func (s *Session) registerHandlers() {
packet.IDAnvilDamage: nil,
packet.IDBlockActorData: &BlockActorDataHandler{},
packet.IDBlockPickRequest: &BlockPickRequestHandler{},
packet.IDBookEdit: &BookEditHandler{},
packet.IDBossEvent: nil,
packet.IDClientCacheBlobStatus: &ClientCacheBlobStatusHandler{},
packet.IDCommandRequest: &CommandRequestHandler{},
Expand Down