-
Notifications
You must be signed in to change notification settings - Fork 143
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
Implement shulker boxes #956
Draft
mmm545
wants to merge
17
commits into
df-mc:master
Choose a base branch
from
mmm545:feature/shulker-box
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Changes from all commits
Commits
Show all changes
17 commits
Select commit
Hold shift + click to select a range
809039a
Implement shulker boxes (no behaviour)
mmm545 d93e10e
Forgot BreakInfo
mmm545 180fd74
Oops
mmm545 dc2f99b
Implement most of shulker box functionality
mmm545 58f7dbb
Oops
mmm545 5bba5e7
Ignore inspection
mmm545 d955b44
Implement custom names
mmm545 c42eb4d
Fixed items disappearing after breaking shulker box
mmm545 60313b9
Make shulker box unstackable
mmm545 bd0063d
Gotta learn how to type good code
mmm545 8e3c84e
Fixed inability to place colored shulker boxes
mmm545 6e2f173
Add docs
mmm545 fd50d99
Refactore allShulkerBox to allShulkerBoxes
mmm545 7655008
Fixed doc typo
mmm545 12091d7
Fixed the sound and closing action being out of sync
mmm545 1f7c0ba
Add missing doc for ScheduledTick
mmm545 3018fb6
Fixed doc typo for CustomName field
mmm545 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,201 @@ | ||||||
package block | ||||||
|
||||||
import ( | ||||||
"fmt" | ||||||
"github.com/df-mc/dragonfly/server/block/cube" | ||||||
"github.com/df-mc/dragonfly/server/internal/nbtconv" | ||||||
"github.com/df-mc/dragonfly/server/item" | ||||||
"github.com/df-mc/dragonfly/server/item/inventory" | ||||||
"github.com/df-mc/dragonfly/server/world" | ||||||
"github.com/df-mc/dragonfly/server/world/sound" | ||||||
"github.com/go-gl/mathgl/mgl64" | ||||||
"math/rand" | ||||||
"strings" | ||||||
"sync" | ||||||
"time" | ||||||
) | ||||||
|
||||||
// ShulkerBox is a dye-able block that stores items. Unlike other blocks, it keeps its contents when broken. | ||||||
type ShulkerBox struct { | ||||||
solid // TODO: I don't think it should be solid | ||||||
// Type is the type of shulker box of the block. | ||||||
Type ShulkerBoxType | ||||||
// Facing is the direction that the shulker box is facing. | ||||||
Facing cube.Face | ||||||
// CustomName is the custom name of the shulker box. This name is displayed when the shulker box is opened, and may | ||||||
// include colour codes. | ||||||
CustomName string | ||||||
|
||||||
inventory *inventory.Inventory | ||||||
viewerMu *sync.RWMutex | ||||||
viewers map[ContainerViewer]struct{} | ||||||
} | ||||||
|
||||||
// NewShulkerBox creates a new initialised shulker box. The inventory is properly initialised. | ||||||
func NewShulkerBox() ShulkerBox { | ||||||
s := ShulkerBox{ | ||||||
viewerMu: new(sync.RWMutex), | ||||||
viewers: make(map[ContainerViewer]struct{}, 1), | ||||||
} | ||||||
|
||||||
s.inventory = inventory.New(27, func(slot int, _, after item.Stack) { | ||||||
s.viewerMu.RLock() | ||||||
defer s.viewerMu.RUnlock() | ||||||
for viewer := range s.viewers { | ||||||
viewer.ViewSlotChange(slot, after) | ||||||
} | ||||||
}) | ||||||
|
||||||
return s | ||||||
} | ||||||
|
||||||
// WithName returns the shulker box after applying a specific name to the block. | ||||||
func (s ShulkerBox) WithName(a ...any) world.Item { | ||||||
s.CustomName = strings.TrimSuffix(fmt.Sprintln(a...), "\n") | ||||||
return s | ||||||
} | ||||||
|
||||||
// AddViewer adds a viewer to the shulker box, so that it is updated whenever the inventory of the shulker box is changed. | ||||||
func (s ShulkerBox) AddViewer(v ContainerViewer, tx *world.Tx, pos cube.Pos) { | ||||||
s.viewerMu.Lock() | ||||||
defer s.viewerMu.Unlock() | ||||||
if len(s.viewers) == 0 { | ||||||
s.open(tx, pos) | ||||||
} | ||||||
|
||||||
s.viewers[v] = struct{}{} | ||||||
} | ||||||
|
||||||
// RemoveViewer removes a viewer from the shulker box, so that slot updates in the inventory are no longer sent to it. | ||||||
func (s ShulkerBox) RemoveViewer(v ContainerViewer, tx *world.Tx, pos cube.Pos) { | ||||||
s.viewerMu.Lock() | ||||||
defer s.viewerMu.Unlock() | ||||||
if len(s.viewers) == 0 { | ||||||
return | ||||||
} | ||||||
delete(s.viewers, v) | ||||||
if len(s.viewers) == 0 { | ||||||
s.close(tx, pos) | ||||||
} | ||||||
} | ||||||
|
||||||
// Inventory returns the inventory of the shulker box. | ||||||
func (s ShulkerBox) Inventory(tx *world.Tx, pos cube.Pos) *inventory.Inventory { | ||||||
return s.inventory | ||||||
} | ||||||
|
||||||
// Activate ... | ||||||
func (s ShulkerBox) Activate(pos cube.Pos, clickedFace cube.Face, tx *world.Tx, u item.User, ctx *item.UseContext) bool { | ||||||
if opener, ok := u.(ContainerOpener); ok { | ||||||
if d, ok := tx.Block(pos.Side(s.Facing)).(LightDiffuser); ok && d.LightDiffusionLevel() <= 2 { | ||||||
opener.OpenBlockContainer(pos, tx) | ||||||
} | ||||||
return true | ||||||
} | ||||||
|
||||||
return false | ||||||
} | ||||||
|
||||||
// UseOnBlock ... | ||||||
func (s ShulkerBox) UseOnBlock(pos cube.Pos, face cube.Face, clickPos mgl64.Vec3, tx *world.Tx, user item.User, ctx *item.UseContext) (used bool) { | ||||||
pos, _, used = firstReplaceable(tx, pos, face, s) | ||||||
if !used { | ||||||
return | ||||||
} | ||||||
|
||||||
if s.inventory == nil { | ||||||
typ, customName := s.Type, s.CustomName | ||||||
//noinspection GoAssignmentToReceiver | ||||||
s = NewShulkerBox() | ||||||
s.Type, s.CustomName = typ, customName | ||||||
} | ||||||
|
||||||
s.Facing = face | ||||||
place(tx, pos, s, user, ctx) | ||||||
return placed(ctx) | ||||||
} | ||||||
|
||||||
// open opens the shulker box, displaying the animation and playing a sound. | ||||||
func (s ShulkerBox) open(tx *world.Tx, pos cube.Pos) { | ||||||
for _, v := range tx.Viewers(pos.Vec3()) { | ||||||
v.ViewBlockAction(pos, OpenAction{}) | ||||||
} | ||||||
tx.PlaySound(pos.Vec3Centre(), sound.ShulkerBoxOpen{}) | ||||||
} | ||||||
|
||||||
// close closes the shulker box, displaying the animation and playing a sound. | ||||||
func (s ShulkerBox) close(tx *world.Tx, pos cube.Pos) { | ||||||
for _, v := range tx.Viewers(pos.Vec3()) { | ||||||
v.ViewBlockAction(pos, CloseAction{}) | ||||||
} | ||||||
tx.ScheduleBlockUpdate(pos, time.Millisecond*50*9) | ||||||
} | ||||||
|
||||||
// ScheduledTick ... | ||||||
func (s ShulkerBox) ScheduledTick(pos cube.Pos, tx *world.Tx, r *rand.Rand) { | ||||||
if len(s.viewers) == 0 { | ||||||
tx.PlaySound(pos.Vec3Centre(), sound.ShulkerBoxClose{}) | ||||||
} | ||||||
} | ||||||
|
||||||
// BreakInfo ... | ||||||
func (s ShulkerBox) BreakInfo() BreakInfo { | ||||||
return newBreakInfo(2, alwaysHarvestable, pickaxeEffective, oneOf(s)).withBlastResistance(10) | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Not needed if blast resistance = hardness * 5 |
||||||
} | ||||||
|
||||||
// MaxCount always returns 1. | ||||||
func (s ShulkerBox) MaxCount() int { | ||||||
return 1 | ||||||
} | ||||||
|
||||||
// EncodeBlock ... | ||||||
func (s ShulkerBox) EncodeBlock() (name string, properties map[string]any) { | ||||||
return "minecraft:" + s.Type.String(), nil | ||||||
} | ||||||
|
||||||
// EncodeItem ... | ||||||
func (s ShulkerBox) EncodeItem() (id string, meta int16) { | ||||||
return "minecraft:" + s.Type.String(), 0 | ||||||
} | ||||||
|
||||||
// DecodeNBT ... | ||||||
func (s ShulkerBox) DecodeNBT(data map[string]any) any { | ||||||
typ := s.Type | ||||||
//noinspection GoAssignmentToReceiver | ||||||
s = NewShulkerBox() | ||||||
s.Type = typ | ||||||
nbtconv.InvFromNBT(s.inventory, nbtconv.Slice(data, "Items")) | ||||||
s.Facing = cube.Face(nbtconv.Uint8(data, "facing")) | ||||||
s.CustomName = nbtconv.String(data, "CustomName") | ||||||
return s | ||||||
} | ||||||
|
||||||
// EncodeNBT .. | ||||||
func (s ShulkerBox) EncodeNBT() map[string]any { | ||||||
if s.inventory == nil { | ||||||
typ, facing, customName := s.Type, s.Facing, s.CustomName | ||||||
//noinspection GoAssignmentToReceiver | ||||||
s = NewShulkerBox() | ||||||
s.Type, s.Facing, s.CustomName = typ, facing, customName | ||||||
} | ||||||
|
||||||
m := map[string]any{ | ||||||
"Items": nbtconv.InvToNBT(s.inventory), | ||||||
"id": "ShulkerBox", | ||||||
"facing": uint8(s.Facing), | ||||||
} | ||||||
|
||||||
if s.CustomName != "" { | ||||||
m["CustomName"] = s.CustomName | ||||||
} | ||||||
return m | ||||||
} | ||||||
|
||||||
// allShulkerBoxes ... | ||||||
func allShulkerBoxes() (shulkerboxes []world.Block) { | ||||||
for _, t := range ShulkerBoxTypes() { | ||||||
shulkerboxes = append(shulkerboxes, ShulkerBox{Type: t}) | ||||||
} | ||||||
|
||||||
return | ||||||
} |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Transparent & water loggable