From 5718d141f28363b8dc85a1559be8720eacd5bbfb Mon Sep 17 00:00:00 2001 From: TwistedAsylumMC Date: Thu, 28 Jul 2022 18:22:54 +0100 Subject: [PATCH 01/83] block: Start of a redstone implementation --- main.go | 69 ++++++++++++++++++- server/block/hash.go | 20 ++++++ server/block/redstone_block.go | 28 ++++++++ server/block/redstone_dust.go | 113 +++++++++++++++++++++++++++++++ server/block/redstone_lamp.go | 59 +++++++++++++++++ server/block/redstone_torch.go | 118 +++++++++++++++++++++++++++++++++ server/block/register.go | 18 +++-- server/world/block.go | 8 +++ server/world/world.go | 54 +++++++++++++++ 9 files changed, 481 insertions(+), 6 deletions(-) create mode 100644 server/block/redstone_block.go create mode 100644 server/block/redstone_dust.go create mode 100644 server/block/redstone_lamp.go create mode 100644 server/block/redstone_torch.go diff --git a/main.go b/main.go index ce94041e2..a7156c76c 100644 --- a/main.go +++ b/main.go @@ -3,11 +3,19 @@ package main import ( "fmt" "github.com/df-mc/dragonfly/server" + "github.com/df-mc/dragonfly/server/block" + "github.com/df-mc/dragonfly/server/block/cube" + "github.com/df-mc/dragonfly/server/block/cube/trace" + "github.com/df-mc/dragonfly/server/player" "github.com/df-mc/dragonfly/server/player/chat" + "github.com/df-mc/dragonfly/server/world" + "github.com/go-gl/mathgl/mgl64" "github.com/pelletier/go-toml" "github.com/sirupsen/logrus" "io/ioutil" + "math" "os" + "time" ) func main() { @@ -28,7 +36,9 @@ func main() { log.Fatalln(err) } - for srv.Accept(nil) { + for srv.Accept(func(p *player.Player) { + p.Handle(newRedstonePlayerHandler(p)) + }) { } } @@ -54,3 +64,60 @@ func readConfig() (server.Config, error) { } return c, nil } + +type redstonePlayerHandler struct { + player.NopHandler + p *player.Player + closeChan chan struct{} +} + +func newRedstonePlayerHandler(p *player.Player) *redstonePlayerHandler { + h := &redstonePlayerHandler{ + p: p, + closeChan: make(chan struct{}, 1), + } + go h.tick() + return h +} + +func (h *redstonePlayerHandler) tick() { + t := time.NewTicker(time.Second / 20) + for { + select { + case <-h.closeChan: + return + case <-t.C: + yaw, pitch := h.p.Rotation() + yaw, pitch = yaw*(math.Pi/180), pitch*(math.Pi/180) + y := -math.Sin(pitch) + xz := math.Cos(pitch) + x := -xz * math.Sin(yaw) + z := xz * math.Cos(yaw) + start := h.p.Position().Add(mgl64.Vec3{0, h.p.EyeHeight()}) + direction := mgl64.Vec3{x, y, z}.Normalize().Mul(50) + var hitBlock world.Block + trace.TraverseBlocks(start, start.Add(direction), func(pos cube.Pos) bool { + b := h.p.World().Block(pos) + if _, ok := b.(block.Air); !ok { + hitBlock = b + return false + } + return true + }) + if hitBlock != nil { + popup := fmt.Sprintf("%T", hitBlock) + switch hitBlock := hitBlock.(type) { + case block.RedstoneDust: + popup += fmt.Sprintf("\nPower: %d", hitBlock.Power) + } + h.p.SendPopup(popup) + } else { + h.p.SendPopup("You are not looking at a block") + } + } + } +} + +func (h *redstonePlayerHandler) HandleQuit() { + close(h.closeChan) +} diff --git a/server/block/hash.go b/server/block/hash.go index c6b6337d1..407d3e33a 100644 --- a/server/block/hash.go +++ b/server/block/hash.go @@ -123,6 +123,10 @@ const ( hashRawCopper hashRawGold hashRawIron + hashRedstoneBlock + hashRedstoneDust + hashRedstoneLamp + hashRedstoneTorch hashReinforcedDeepslate hashSand hashSandstone @@ -642,6 +646,22 @@ func (RawIron) Hash() uint64 { return hashRawIron } +func (RedstoneBlock) Hash() uint64 { + return hashRedstoneBlock +} + +func (r RedstoneDust) Hash() uint64 { + return hashRedstoneDust | uint64(r.Power)<<8 +} + +func (l RedstoneLamp) Hash() uint64 { + return hashRedstoneLamp | uint64(boolByte(l.Lit))<<8 +} + +func (t RedstoneTorch) Hash() uint64 { + return hashRedstoneTorch | uint64(t.Facing)<<8 | uint64(boolByte(t.Lit))<<11 +} + func (ReinforcedDeepslate) Hash() uint64 { return hashReinforcedDeepslate } diff --git a/server/block/redstone_block.go b/server/block/redstone_block.go new file mode 100644 index 000000000..2379d4f1b --- /dev/null +++ b/server/block/redstone_block.go @@ -0,0 +1,28 @@ +package block + +import ( + "github.com/df-mc/dragonfly/server/block/cube" + "github.com/df-mc/dragonfly/server/world" +) + +type RedstoneBlock struct{ solid } + +// EncodeItem ... +func (b RedstoneBlock) EncodeItem() (name string, meta int16) { + return "minecraft:redstone_block", 0 +} + +// EncodeBlock ... +func (b RedstoneBlock) EncodeBlock() (string, map[string]any) { + return "minecraft:redstone_block", nil +} + +// WeakPower ... +func (b RedstoneBlock) WeakPower(cube.Pos, cube.Face, *world.World) int { + return 15 +} + +// StrongPower ... +func (b RedstoneBlock) StrongPower(cube.Pos, cube.Face, *world.World) int { + return 0 +} diff --git a/server/block/redstone_dust.go b/server/block/redstone_dust.go new file mode 100644 index 000000000..b45fdd4b4 --- /dev/null +++ b/server/block/redstone_dust.go @@ -0,0 +1,113 @@ +package block + +import ( + "github.com/df-mc/dragonfly/server/block/cube" + "github.com/df-mc/dragonfly/server/block/model" + "github.com/df-mc/dragonfly/server/item" + "github.com/df-mc/dragonfly/server/world" + "github.com/go-gl/mathgl/mgl64" +) + +type RedstoneDust struct { + empty + transparent + Power int + emitting bool +} + +// EncodeItem ... +func (r RedstoneDust) EncodeItem() (name string, meta int16) { + return "minecraft:redstone", 0 +} + +// EncodeBlock ... +func (r RedstoneDust) EncodeBlock() (string, map[string]any) { + return "minecraft:redstone_wire", map[string]any{ + "redstone_signal": int32(r.Power), + } +} + +// UseOnBlock ... +func (r RedstoneDust) 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, r) + if !used { + return + } + belowPos := pos.Side(cube.FaceDown) + if !w.Block(belowPos).Model().FaceSolid(belowPos, cube.FaceUp, w) { + return + } + r.emitting = true + r.Power = r.receivedPower(pos, w) + place(w, pos, r, user, ctx) + return placed(ctx) +} + +// NeighbourUpdateTick ... +func (r RedstoneDust) NeighbourUpdateTick(pos, _ cube.Pos, w *world.World) { + power := r.receivedPower(pos, w) + if r.Power != power { + r.Power = power + w.SetBlock(pos, r, nil) + } +} + +// WeakPower ... +func (r RedstoneDust) WeakPower(_ cube.Pos, face cube.Face, _ *world.World) int { + if !r.emitting || face == cube.FaceDown { + return 0 + } + if r.Power > 0 { + // TODO: Some connectivity logic + return r.Power + } + return 0 +} + +// StrongPower ... +func (r RedstoneDust) StrongPower(pos cube.Pos, face cube.Face, w *world.World) int { + if !r.emitting { + return 0 + } + return r.WeakPower(pos, face, w) +} + +// receivedPower returns the highest level of received redstone power at the provided position. +func (r RedstoneDust) receivedPower(pos cube.Pos, w *world.World) int { + r.emitting = false + received := w.ReceivedRedstonePower(pos) + r.emitting = true + var power int + if received < 15 { + _, solidAbove := w.Block(pos.Side(cube.FaceUp)).Model().(model.Solid) + for _, face := range cube.HorizontalFaces() { + sidePos := pos.Side(face) + received = max(received, r.checkPower(sidePos, w)) + _, sideSolid := w.Block(sidePos).Model().(model.Solid) + if sideSolid && !solidAbove { + received = max(received, r.checkPower(sidePos.Side(cube.FaceUp), w)) + } else if !sideSolid { + received = max(received, r.checkPower(sidePos.Side(cube.FaceDown), w)) + } + } + } + return max(power, received-1) +} + +// checkPower attempts to return the power level of the redstone dust at the provided position if it exists. If there is +// no redstone dust at the position, 0 is returned. +func (r RedstoneDust) checkPower(pos cube.Pos, w *world.World) int { + block := w.Block(pos) + if b, ok := block.(RedstoneDust); ok { + return b.Power + } + return 0 +} + +// allRedstoneDust returns a list of all redstone dust states. +func allRedstoneDust() (all []world.Block) { + for i := 0; i < 16; i++ { + all = append(all, RedstoneDust{Power: i}) + } + return +} diff --git a/server/block/redstone_lamp.go b/server/block/redstone_lamp.go new file mode 100644 index 000000000..278e168cc --- /dev/null +++ b/server/block/redstone_lamp.go @@ -0,0 +1,59 @@ +package block + +import ( + "github.com/df-mc/dragonfly/server/block/cube" + "github.com/df-mc/dragonfly/server/item" + "github.com/df-mc/dragonfly/server/world" + "github.com/go-gl/mathgl/mgl64" +) + +type RedstoneLamp struct { + solid + // Lit is if the redstone lamp is lit and emitting light. + Lit bool +} + +// BreakInfo ... +func (l RedstoneLamp) BreakInfo() BreakInfo { + return newBreakInfo(0.3, alwaysHarvestable, nothingEffective, oneOf(l)) +} + +// LightEmissionLevel ... +func (l RedstoneLamp) LightEmissionLevel() uint8 { + if l.Lit { + return 15 + } + return 0 +} + +// EncodeItem ... +func (l RedstoneLamp) EncodeItem() (name string, meta int16) { + return "minecraft:redstone_lamp", 0 +} + +// EncodeBlock ... +func (l RedstoneLamp) EncodeBlock() (string, map[string]any) { + if l.Lit { + return "minecraft:lit_redstone_lamp", nil + } + return "minecraft:redstone_lamp", nil +} + +// UseOnBlock ... +func (l RedstoneLamp) 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 + } + l.Lit = w.IsReceivingRedstonePower(pos) + place(w, pos, l, user, ctx) + return placed(ctx) +} + +// NeighbourUpdateTick ... +func (l RedstoneLamp) NeighbourUpdateTick(pos, _ cube.Pos, w *world.World) { + if l.Lit != w.IsReceivingRedstonePower(pos) { + l.Lit = !l.Lit + w.SetBlock(pos, l, nil) + } +} diff --git a/server/block/redstone_torch.go b/server/block/redstone_torch.go new file mode 100644 index 000000000..99fa95ce5 --- /dev/null +++ b/server/block/redstone_torch.go @@ -0,0 +1,118 @@ +package block + +import ( + "github.com/df-mc/dragonfly/server/block/cube" + "github.com/df-mc/dragonfly/server/item" + "github.com/df-mc/dragonfly/server/world" + "github.com/go-gl/mathgl/mgl64" +) + +// RedstoneTorch is a non-solid blocks that emits little light and also a full-strength redstone signal when lit. +type RedstoneTorch struct { + transparent + empty + + // Facing is the direction from the torch to the block. + Facing cube.Face + // Lit is if the redstone torch is lit and emitting power. + Lit bool +} + +// BreakInfo ... +func (t RedstoneTorch) BreakInfo() BreakInfo { + return newBreakInfo(0, alwaysHarvestable, nothingEffective, oneOf(t)) +} + +// LightEmissionLevel ... +func (t RedstoneTorch) LightEmissionLevel() uint8 { + if t.Lit { + return 7 + } + return 0 +} + +// UseOnBlock ... +func (t RedstoneTorch) UseOnBlock(pos cube.Pos, face cube.Face, _ mgl64.Vec3, w *world.World, user item.User, ctx *item.UseContext) bool { + pos, face, used := firstReplaceable(w, pos, face, t) + if !used { + return false + } + if face == cube.FaceDown { + return false + } + if _, ok := w.Block(pos).(world.Liquid); ok { + return false + } + if !w.Block(pos.Side(face.Opposite())).Model().FaceSolid(pos.Side(face.Opposite()), face, w) { + found := false + for _, i := range []cube.Face{cube.FaceSouth, cube.FaceWest, cube.FaceNorth, cube.FaceEast, cube.FaceDown} { + if w.Block(pos.Side(i)).Model().FaceSolid(pos.Side(i), i.Opposite(), w) { + found = true + face = i.Opposite() + break + } + } + if !found { + return false + } + } + t.Facing = face.Opposite() + t.Lit = true + + place(w, pos, t, user, ctx) + return placed(ctx) +} + +// NeighbourUpdateTick ... +func (t RedstoneTorch) NeighbourUpdateTick(pos, _ cube.Pos, w *world.World) { + if !w.Block(pos.Side(t.Facing)).Model().FaceSolid(pos.Side(t.Facing), t.Facing.Opposite(), w) { + w.SetBlock(pos, nil, nil) + } +} + +// HasLiquidDrops ... +func (t RedstoneTorch) HasLiquidDrops() bool { + return true +} + +// EncodeItem ... +func (t RedstoneTorch) EncodeItem() (name string, meta int16) { + return "minecraft:redstone_torch", 0 +} + +// EncodeBlock ... +func (t RedstoneTorch) EncodeBlock() (name string, properties map[string]any) { + face := t.Facing.String() + if t.Facing == cube.FaceDown { + face = "top" + } + if t.Lit { + return "minecraft:redstone_torch", map[string]any{"torch_facing_direction": face} + } + return "minecraft:unlit_redstone_torch", map[string]any{"torch_facing_direction": face} +} + +// WeakPower ... +func (t RedstoneTorch) WeakPower(_ cube.Pos, face cube.Face, _ *world.World) int { + if t.Lit && face != cube.FaceUp { + return 15 + } + return 0 +} + +// StrongPower ... +func (t RedstoneTorch) StrongPower(cube.Pos, cube.Face, *world.World) int { + return 0 +} + +// allRedstoneTorches ... +func allRedstoneTorches() (all []world.Block) { + for i := cube.Face(0); i < 6; i++ { + if i == cube.FaceUp { + continue + } + all = append(all, RedstoneTorch{Facing: i, Lit: true}) + all = append(all, RedstoneTorch{Facing: i}) + } + return +} diff --git a/server/block/register.go b/server/block/register.go index 61eb9b69f..97bf3582d 100644 --- a/server/block/register.go +++ b/server/block/register.go @@ -64,10 +64,10 @@ func init() { world.RegisterBlock(NetherGoldOre{}) world.RegisterBlock(NetherQuartzOre{}) world.RegisterBlock(NetherSprouts{}) + world.RegisterBlock(NetherWartBlock{Warped: true}) + world.RegisterBlock(NetherWartBlock{}) world.RegisterBlock(Netherite{}) world.RegisterBlock(Netherrack{}) - world.RegisterBlock(NetherWartBlock{}) - world.RegisterBlock(NetherWartBlock{Warped: true}) world.RegisterBlock(Note{}) world.RegisterBlock(Obsidian{Crying: true}) world.RegisterBlock(Obsidian{}) @@ -78,6 +78,9 @@ func init() { world.RegisterBlock(RawCopper{}) world.RegisterBlock(RawGold{}) world.RegisterBlock(RawIron{}) + world.RegisterBlock(RedstoneBlock{}) + world.RegisterBlock(RedstoneLamp{Lit: true}) + world.RegisterBlock(RedstoneLamp{}) world.RegisterBlock(ReinforcedDeepslate{}) world.RegisterBlock(Sand{Red: true}) world.RegisterBlock(Sand{}) @@ -157,6 +160,8 @@ func init() { registerAll(allPurpurs()) registerAll(allQuartz()) registerAll(allQuartzStairs()) + registerAll(allRedstoneDust()) + registerAll(allRedstoneTorches()) registerAll(allSandstoneSlabs()) registerAll(allSandstoneStairs()) registerAll(allSandstones()) @@ -261,11 +266,11 @@ func init() { world.RegisterItem(NetherGoldOre{}) world.RegisterItem(NetherQuartzOre{}) world.RegisterItem(NetherSprouts{}) + world.RegisterItem(NetherWartBlock{Warped: true}) + world.RegisterItem(NetherWartBlock{}) world.RegisterItem(NetherWart{}) world.RegisterItem(Netherite{}) world.RegisterItem(Netherrack{}) - world.RegisterItem(NetherWartBlock{}) - world.RegisterItem(NetherWartBlock{Warped: true}) world.RegisterItem(Note{Pitch: 24}) world.RegisterItem(Obsidian{Crying: true}) world.RegisterItem(Obsidian{}) @@ -276,8 +281,8 @@ func init() { world.RegisterItem(PumpkinSeeds{}) world.RegisterItem(Pumpkin{Carved: true}) world.RegisterItem(Pumpkin{}) - world.RegisterItem(Purpur{}) world.RegisterItem(PurpurPillar{}) + world.RegisterItem(Purpur{}) world.RegisterItem(QuartzBricks{}) world.RegisterItem(QuartzPillar{}) world.RegisterItem(QuartzStairs{Smooth: true}) @@ -287,6 +292,9 @@ func init() { world.RegisterItem(RawCopper{}) world.RegisterItem(RawGold{}) world.RegisterItem(RawIron{}) + world.RegisterItem(RedstoneBlock{}) + world.RegisterItem(RedstoneDust{}) + world.RegisterItem(RedstoneTorch{}) world.RegisterItem(ReinforcedDeepslate{}) world.RegisterItem(Sand{Red: true}) world.RegisterItem(Sand{}) diff --git a/server/world/block.go b/server/world/block.go index f89e873f3..8535ba6f4 100644 --- a/server/world/block.go +++ b/server/world/block.go @@ -46,6 +46,14 @@ type Liquid interface { Harden(pos cube.Pos, w *World, flownIntoBy *cube.Pos) bool } +// Conductor represents a block that can conduct a redstone signal. +type Conductor interface { + // WeakPower returns the power from a partial source and has limited usage. + WeakPower(pos cube.Pos, face cube.Face, w *World) int + // StrongPower returns the power from a full source and can be passed to any redstone component. + StrongPower(pos cube.Pos, face cube.Face, w *World) int +} + // hashes holds a list of runtime IDs indexed by the hash of the Block that implements the blocks pointed to by those // runtime IDs. It is used to look up a block's runtime ID quickly. var hashes = intintmap.New(7000, 0.999) diff --git a/server/world/world.go b/server/world/world.go index 0fbce9202..5fcf64bfe 100644 --- a/server/world/world.go +++ b/server/world/world.go @@ -989,6 +989,52 @@ func (w *World) PortalDestination(dim Dimension) *World { return w } +// EmittedRedstonePower returns the level of redstone power being emitted from a position to the provided face. +func (w *World) EmittedRedstonePower(pos cube.Pos, face cube.Face) int { + block := w.Block(pos) + if conductor, ok := block.(Conductor); ok { + power := conductor.WeakPower(pos, face, w) + for _, f := range cube.Faces() { + if !block.Model().FaceSolid(pos, f, w) { + return power + } + } + return max(power, w.ReceivedStrongRedstonePower(pos)) + } + return 0 +} + +// ReceivedStrongRedstonePower returns the level of strong redstone power being received at the provided position. +func (w *World) ReceivedStrongRedstonePower(pos cube.Pos) (power int) { + for _, face := range cube.Faces() { + if conductor, ok := w.Block(pos.Side(face)).(Conductor); ok { + power = max(power, conductor.StrongPower(pos.Side(face), face.Opposite(), w)) + if power >= 15 { + return power + } + } + } + return +} + +// IsReceivingRedstonePower returns if the provided position is receiving any level of redstone power. +func (w *World) IsReceivingRedstonePower(pos cube.Pos) bool { + return w.ReceivedRedstonePower(pos) > 0 +} + +// ReceivedRedstonePower returns the highest level of redstone power received by the provided position. +func (w *World) ReceivedRedstonePower(pos cube.Pos) (power int) { + for _, face := range cube.Faces() { + emitted := w.EmittedRedstonePower(pos.Side(face), face.Opposite()) + if emitted >= 15 { + return emitted + } else if emitted > power { + power = emitted + } + } + return +} + // Close closes the world and saves all chunks currently loaded. func (w *World) Close() error { if w == nil { @@ -1422,3 +1468,11 @@ func (c *chunkData) Entities() []Entity { func newChunkData(c *chunk.Chunk) *chunkData { return &chunkData{Chunk: c, e: map[cube.Pos]Block{}} } + +// max returns the max of two integers. +func max(x, y int) int { + if x > y { + return x + } + return y +} From a0f3b30b79c7b9b0259ac30179dbb7362b342f9e Mon Sep 17 00:00:00 2001 From: TwistedAsylumMC Date: Thu, 28 Jul 2022 18:28:31 +0100 Subject: [PATCH 02/83] main.go: Use entity.DirectionVector() --- main.go | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/main.go b/main.go index a7156c76c..22a14be2b 100644 --- a/main.go +++ b/main.go @@ -6,6 +6,7 @@ import ( "github.com/df-mc/dragonfly/server/block" "github.com/df-mc/dragonfly/server/block/cube" "github.com/df-mc/dragonfly/server/block/cube/trace" + "github.com/df-mc/dragonfly/server/entity" "github.com/df-mc/dragonfly/server/player" "github.com/df-mc/dragonfly/server/player/chat" "github.com/df-mc/dragonfly/server/world" @@ -13,7 +14,6 @@ import ( "github.com/pelletier/go-toml" "github.com/sirupsen/logrus" "io/ioutil" - "math" "os" "time" ) @@ -87,16 +87,10 @@ func (h *redstonePlayerHandler) tick() { case <-h.closeChan: return case <-t.C: - yaw, pitch := h.p.Rotation() - yaw, pitch = yaw*(math.Pi/180), pitch*(math.Pi/180) - y := -math.Sin(pitch) - xz := math.Cos(pitch) - x := -xz * math.Sin(yaw) - z := xz * math.Cos(yaw) start := h.p.Position().Add(mgl64.Vec3{0, h.p.EyeHeight()}) - direction := mgl64.Vec3{x, y, z}.Normalize().Mul(50) + end := start.Add(entity.DirectionVector(h.p).Mul(50)) var hitBlock world.Block - trace.TraverseBlocks(start, start.Add(direction), func(pos cube.Pos) bool { + trace.TraverseBlocks(start, end, func(pos cube.Pos) bool { b := h.p.World().Block(pos) if _, ok := b.(block.Air); !ok { hitBlock = b From 0261190b667996386b7c42dcca2baedb8a5573cf Mon Sep 17 00:00:00 2001 From: JustTal Date: Fri, 29 Jul 2022 21:07:42 -0500 Subject: [PATCH 03/83] block/redstone_dust.go: Various improvements. --- server/block/redstone_dust.go | 53 ++++++++++++++++++++++++---------- server/block/redstone_lamp.go | 6 ++-- server/block/redstone_torch.go | 2 +- server/world/block.go | 2 ++ server/world/world.go | 41 +++++++++++++------------- 5 files changed, 64 insertions(+), 40 deletions(-) diff --git a/server/block/redstone_dust.go b/server/block/redstone_dust.go index b45fdd4b4..7f3af48ef 100644 --- a/server/block/redstone_dust.go +++ b/server/block/redstone_dust.go @@ -3,16 +3,18 @@ package block import ( "github.com/df-mc/dragonfly/server/block/cube" "github.com/df-mc/dragonfly/server/block/model" + "github.com/df-mc/dragonfly/server/internal/sliceutil" "github.com/df-mc/dragonfly/server/item" "github.com/df-mc/dragonfly/server/world" "github.com/go-gl/mathgl/mgl64" + "golang.org/x/exp/slices" ) type RedstoneDust struct { empty transparent - Power int - emitting bool + Power int + disableEmitting bool } // EncodeItem ... @@ -37,7 +39,6 @@ func (r RedstoneDust) UseOnBlock(pos cube.Pos, face cube.Face, _ mgl64.Vec3, w * if !w.Block(belowPos).Model().FaceSolid(belowPos, cube.FaceUp, w) { return } - r.emitting = true r.Power = r.receivedPower(pos, w) place(w, pos, r, user, ctx) return placed(ctx) @@ -49,16 +50,30 @@ func (r RedstoneDust) NeighbourUpdateTick(pos, _ cube.Pos, w *world.World) { if r.Power != power { r.Power = power w.SetBlock(pos, r, nil) + for _, face := range cube.Faces() { + sidePos := pos.Side(face) + if n, ok := w.Block(sidePos).(world.Conductor); ok { + n.NeighbourUpdateTick(sidePos, pos, w) + } + } } } // WeakPower ... -func (r RedstoneDust) WeakPower(_ cube.Pos, face cube.Face, _ *world.World) int { - if !r.emitting || face == cube.FaceDown { +func (r RedstoneDust) WeakPower(pos cube.Pos, side cube.Face, w *world.World) int { + if r.disableEmitting || side == cube.FaceDown { return 0 } - if r.Power > 0 { - // TODO: Some connectivity logic + if side == cube.FaceUp { + return r.Power + } + + faces := sliceutil.Filter(cube.HorizontalFaces(), func(face cube.Face) bool { + return r.powers(pos, face, w) + }) + if side.Axis() != cube.Y && len(faces) == 0 { + return r.Power + } else if slices.Contains(faces, side) && !slices.Contains(faces, side.RotateLeft()) && !slices.Contains(faces, side.RotateRight()) { return r.Power } return 0 @@ -66,7 +81,7 @@ func (r RedstoneDust) WeakPower(_ cube.Pos, face cube.Face, _ *world.World) int // StrongPower ... func (r RedstoneDust) StrongPower(pos cube.Pos, face cube.Face, w *world.World) int { - if !r.emitting { + if r.disableEmitting { return 0 } return r.WeakPower(pos, face, w) @@ -74,24 +89,24 @@ func (r RedstoneDust) StrongPower(pos cube.Pos, face cube.Face, w *world.World) // receivedPower returns the highest level of received redstone power at the provided position. func (r RedstoneDust) receivedPower(pos cube.Pos, w *world.World) int { - r.emitting = false + r.disableEmitting = true received := w.ReceivedRedstonePower(pos) - r.emitting = true + r.disableEmitting = false + var power int if received < 15 { _, solidAbove := w.Block(pos.Side(cube.FaceUp)).Model().(model.Solid) for _, face := range cube.HorizontalFaces() { sidePos := pos.Side(face) - received = max(received, r.checkPower(sidePos, w)) - _, sideSolid := w.Block(sidePos).Model().(model.Solid) - if sideSolid && !solidAbove { - received = max(received, r.checkPower(sidePos.Side(cube.FaceUp), w)) + power = max(power, r.checkPower(sidePos, w)) + if _, sideSolid := w.Block(sidePos).Model().(model.Solid); sideSolid && !solidAbove { + power = max(power, r.checkPower(sidePos.Side(cube.FaceUp), w)) } else if !sideSolid { - received = max(received, r.checkPower(sidePos.Side(cube.FaceDown), w)) + power = max(power, r.checkPower(sidePos.Side(cube.FaceDown), w)) } } } - return max(power, received-1) + return max(received, power-1) } // checkPower attempts to return the power level of the redstone dust at the provided position if it exists. If there is @@ -104,6 +119,12 @@ func (r RedstoneDust) checkPower(pos cube.Pos, w *world.World) int { return 0 } +// powers returns true if the dust powers the given face. +func (r RedstoneDust) powers(pos cube.Pos, face cube.Face, w *world.World) bool { + // TODO + return true +} + // allRedstoneDust returns a list of all redstone dust states. func allRedstoneDust() (all []world.Block) { for i := 0; i < 16; i++ { diff --git a/server/block/redstone_lamp.go b/server/block/redstone_lamp.go index 278e168cc..e69becd4f 100644 --- a/server/block/redstone_lamp.go +++ b/server/block/redstone_lamp.go @@ -9,7 +9,7 @@ import ( type RedstoneLamp struct { solid - // Lit is if the redstone lamp is lit and emitting light. + // Lit is if the redstone lamp is lit and disableEmitting light. Lit bool } @@ -45,14 +45,14 @@ func (l RedstoneLamp) UseOnBlock(pos cube.Pos, face cube.Face, _ mgl64.Vec3, w * if !used { return } - l.Lit = w.IsReceivingRedstonePower(pos) + l.Lit = w.ReceivingRedstonePower(pos) place(w, pos, l, user, ctx) return placed(ctx) } // NeighbourUpdateTick ... func (l RedstoneLamp) NeighbourUpdateTick(pos, _ cube.Pos, w *world.World) { - if l.Lit != w.IsReceivingRedstonePower(pos) { + if l.Lit != w.ReceivingRedstonePower(pos) { l.Lit = !l.Lit w.SetBlock(pos, l, nil) } diff --git a/server/block/redstone_torch.go b/server/block/redstone_torch.go index 99fa95ce5..c72a88bbd 100644 --- a/server/block/redstone_torch.go +++ b/server/block/redstone_torch.go @@ -14,7 +14,7 @@ type RedstoneTorch struct { // Facing is the direction from the torch to the block. Facing cube.Face - // Lit is if the redstone torch is lit and emitting power. + // Lit is if the redstone torch is lit and disableEmitting power. Lit bool } diff --git a/server/world/block.go b/server/world/block.go index 8535ba6f4..da81744a1 100644 --- a/server/world/block.go +++ b/server/world/block.go @@ -48,6 +48,8 @@ type Liquid interface { // Conductor represents a block that can conduct a redstone signal. type Conductor interface { + Block + NeighbourUpdateTicker // WeakPower returns the power from a partial source and has limited usage. WeakPower(pos cube.Pos, face cube.Face, w *World) int // StrongPower returns the power from a full source and can be passed to any redstone component. diff --git a/server/world/world.go b/server/world/world.go index 5fcf64bfe..3c9a16eb1 100644 --- a/server/world/world.go +++ b/server/world/world.go @@ -991,34 +991,36 @@ func (w *World) PortalDestination(dim Dimension) *World { // EmittedRedstonePower returns the level of redstone power being emitted from a position to the provided face. func (w *World) EmittedRedstonePower(pos cube.Pos, face cube.Face) int { - block := w.Block(pos) - if conductor, ok := block.(Conductor); ok { - power := conductor.WeakPower(pos, face, w) - for _, f := range cube.Faces() { - if !block.Model().FaceSolid(pos, f, w) { - return power - } + c, ok := w.Block(pos).(Conductor) + if !ok { + return 0 + } + power := c.WeakPower(pos, face, w) + for _, f := range cube.Faces() { + if !c.Model().FaceSolid(pos, f, w) { + return power } - return max(power, w.ReceivedStrongRedstonePower(pos)) } - return 0 + return max(power, w.ReceivedStrongRedstonePower(pos)) } // ReceivedStrongRedstonePower returns the level of strong redstone power being received at the provided position. func (w *World) ReceivedStrongRedstonePower(pos cube.Pos) (power int) { for _, face := range cube.Faces() { - if conductor, ok := w.Block(pos.Side(face)).(Conductor); ok { - power = max(power, conductor.StrongPower(pos.Side(face), face.Opposite(), w)) - if power >= 15 { - return power - } + c, ok := w.Block(pos.Side(face)).(Conductor) + if !ok { + continue + } + power = max(power, c.StrongPower(pos.Side(face), face.Opposite(), w)) + if power >= 15 { + return power } } - return + return power } -// IsReceivingRedstonePower returns if the provided position is receiving any level of redstone power. -func (w *World) IsReceivingRedstonePower(pos cube.Pos) bool { +// ReceivingRedstonePower returns if the provided position is receiving any level of redstone power. +func (w *World) ReceivingRedstonePower(pos cube.Pos) bool { return w.ReceivedRedstonePower(pos) > 0 } @@ -1028,11 +1030,10 @@ func (w *World) ReceivedRedstonePower(pos cube.Pos) (power int) { emitted := w.EmittedRedstonePower(pos.Side(face), face.Opposite()) if emitted >= 15 { return emitted - } else if emitted > power { - power = emitted } + power = max(emitted, power) } - return + return power } // Close closes the world and saves all chunks currently loaded. From 9d95a78be07ae61080807ee1e07b3892242a4d38 Mon Sep 17 00:00:00 2001 From: JustTal Date: Fri, 29 Jul 2022 21:14:24 -0500 Subject: [PATCH 04/83] block/redstone_dust.go: Allow any side on the Y axis. --- server/block/redstone_dust.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/block/redstone_dust.go b/server/block/redstone_dust.go index 7f3af48ef..2a6402e4f 100644 --- a/server/block/redstone_dust.go +++ b/server/block/redstone_dust.go @@ -61,10 +61,10 @@ func (r RedstoneDust) NeighbourUpdateTick(pos, _ cube.Pos, w *world.World) { // WeakPower ... func (r RedstoneDust) WeakPower(pos cube.Pos, side cube.Face, w *world.World) int { - if r.disableEmitting || side == cube.FaceDown { + if r.disableEmitting { return 0 } - if side == cube.FaceUp { + if side.Axis() == cube.Y { return r.Power } From f0d95ec4c467f2989341d149bbffd9c3b1f8d50b Mon Sep 17 00:00:00 2001 From: JustTal Date: Fri, 29 Jul 2022 21:14:45 -0500 Subject: [PATCH 05/83] block/redstone_dust.go: Removed redundant check. --- server/block/redstone_dust.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/server/block/redstone_dust.go b/server/block/redstone_dust.go index 2a6402e4f..f77c61864 100644 --- a/server/block/redstone_dust.go +++ b/server/block/redstone_dust.go @@ -71,9 +71,10 @@ func (r RedstoneDust) WeakPower(pos cube.Pos, side cube.Face, w *world.World) in faces := sliceutil.Filter(cube.HorizontalFaces(), func(face cube.Face) bool { return r.powers(pos, face, w) }) - if side.Axis() != cube.Y && len(faces) == 0 { + if len(faces) == 0 { return r.Power - } else if slices.Contains(faces, side) && !slices.Contains(faces, side.RotateLeft()) && !slices.Contains(faces, side.RotateRight()) { + } + if slices.Contains(faces, side) && !slices.Contains(faces, side.RotateLeft()) && !slices.Contains(faces, side.RotateRight()) { return r.Power } return 0 From ab2ac474f6e28d9482151773e4342be793aed8d1 Mon Sep 17 00:00:00 2001 From: JustTal Date: Sat, 30 Jul 2022 16:21:19 -0500 Subject: [PATCH 06/83] block/redstone_dust.go: Various improvements. --- main.go | 7 +++- server/block/redstone_dust.go | 65 +++++++++++++++++++++++++--------- server/block/redstone_lamp.go | 2 +- server/block/redstone_torch.go | 2 +- server/world/block.go | 1 - server/world/world.go | 21 ++++++----- 6 files changed, 66 insertions(+), 32 deletions(-) diff --git a/main.go b/main.go index 22a14be2b..de1898341 100644 --- a/main.go +++ b/main.go @@ -76,6 +76,7 @@ func newRedstonePlayerHandler(p *player.Player) *redstonePlayerHandler { p: p, closeChan: make(chan struct{}, 1), } + p.ShowCoordinates() go h.tick() return h } @@ -87,13 +88,16 @@ func (h *redstonePlayerHandler) tick() { case <-h.closeChan: return case <-t.C: + w := h.p.World() start := h.p.Position().Add(mgl64.Vec3{0, h.p.EyeHeight()}) end := start.Add(entity.DirectionVector(h.p).Mul(50)) var hitBlock world.Block + var hitPos cube.Pos trace.TraverseBlocks(start, end, func(pos cube.Pos) bool { - b := h.p.World().Block(pos) + b := w.Block(pos) if _, ok := b.(block.Air); !ok { hitBlock = b + hitPos = pos return false } return true @@ -104,6 +108,7 @@ func (h *redstonePlayerHandler) tick() { case block.RedstoneDust: popup += fmt.Sprintf("\nPower: %d", hitBlock.Power) } + popup += fmt.Sprintf("\nCalculated Power: %d", w.ReceivedRedstonePower(hitPos)) h.p.SendPopup(popup) } else { h.p.SendPopup("You are not looking at a block") diff --git a/server/block/redstone_dust.go b/server/block/redstone_dust.go index f77c61864..489c41e1a 100644 --- a/server/block/redstone_dust.go +++ b/server/block/redstone_dust.go @@ -1,6 +1,7 @@ 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/sliceutil" @@ -8,13 +9,13 @@ import ( "github.com/df-mc/dragonfly/server/world" "github.com/go-gl/mathgl/mgl64" "golang.org/x/exp/slices" + "sync" ) type RedstoneDust struct { empty transparent - Power int - disableEmitting bool + Power int } // EncodeItem ... @@ -52,24 +53,28 @@ func (r RedstoneDust) NeighbourUpdateTick(pos, _ cube.Pos, w *world.World) { w.SetBlock(pos, r, nil) for _, face := range cube.Faces() { sidePos := pos.Side(face) - if n, ok := w.Block(sidePos).(world.Conductor); ok { - n.NeighbourUpdateTick(sidePos, pos, w) + if c, ok := w.Block(sidePos).(world.Conductor); ok { + if n, ok := c.(world.NeighbourUpdateTicker); ok { + n.NeighbourUpdateTick(sidePos, pos, w) + } } } } } +// disabledEmitters ... +var disabledEmitters sync.Map + // WeakPower ... func (r RedstoneDust) WeakPower(pos cube.Pos, side cube.Face, w *world.World) int { - if r.disableEmitting { + if _, ok := disabledEmitters.Load(pos); ok || side == cube.FaceDown { return 0 } - if side.Axis() == cube.Y { + if side == cube.FaceUp { return r.Power } - faces := sliceutil.Filter(cube.HorizontalFaces(), func(face cube.Face) bool { - return r.powers(pos, face, w) + return r.connection(pos, face, w) }) if len(faces) == 0 { return r.Power @@ -82,7 +87,7 @@ func (r RedstoneDust) WeakPower(pos cube.Pos, side cube.Face, w *world.World) in // StrongPower ... func (r RedstoneDust) StrongPower(pos cube.Pos, face cube.Face, w *world.World) int { - if r.disableEmitting { + if _, ok := disabledEmitters.Load(pos); ok { return 0 } return r.WeakPower(pos, face, w) @@ -90,9 +95,9 @@ func (r RedstoneDust) StrongPower(pos cube.Pos, face cube.Face, w *world.World) // receivedPower returns the highest level of received redstone power at the provided position. func (r RedstoneDust) receivedPower(pos cube.Pos, w *world.World) int { - r.disableEmitting = true + disabledEmitters.Store(pos, struct{}{}) received := w.ReceivedRedstonePower(pos) - r.disableEmitting = false + disabledEmitters.Delete(pos) var power int if received < 15 { @@ -107,23 +112,49 @@ func (r RedstoneDust) receivedPower(pos cube.Pos, w *world.World) int { } } } + fmt.Printf("Received: %v\n", received) + fmt.Printf("Power: %v\n", power-1) + fmt.Println(max(received, power-1)) return max(received, power-1) } // checkPower attempts to return the power level of the redstone dust at the provided position if it exists. If there is // no redstone dust at the position, 0 is returned. func (r RedstoneDust) checkPower(pos cube.Pos, w *world.World) int { - block := w.Block(pos) - if b, ok := block.(RedstoneDust); ok { + if b, ok := w.Block(pos).(RedstoneDust); ok { return b.Power } return 0 } -// powers returns true if the dust powers the given face. -func (r RedstoneDust) powers(pos cube.Pos, face cube.Face, w *world.World) bool { - // TODO - return true +// connection returns true if the dust connects to the given face. +func (r RedstoneDust) connection(pos cube.Pos, face cube.Face, w *world.World) bool { + sidePos := pos.Side(face) + sideBlock := w.Block(sidePos) + if _, solidAbove := w.Block(pos.Side(cube.FaceUp)).Model().(model.Solid); !solidAbove && r.canRunOnTop(w, sidePos, sideBlock) && r.connectsTo(w.Block(sidePos.Side(cube.FaceUp)), false) { + return true + } + _, sideSolid := sideBlock.Model().(model.Solid) + return r.connectsTo(sideBlock, true) || !sideSolid && r.connectsTo(w.Block(sidePos.Side(cube.FaceDown)), false) +} + +// connectsTo ... +func (r RedstoneDust) connectsTo(block world.Block, hasFace bool) bool { + switch block.(type) { + case RedstoneDust: + return true + // TODO: Repeaters, observers + } + if _, ok := block.(world.Conductor); ok { + return hasFace + } + return false +} + +// canRunOnTop ... +func (r RedstoneDust) canRunOnTop(w *world.World, pos cube.Pos, block world.Block) bool { + // TODO: Hoppers. + return block.Model().FaceSolid(pos, cube.FaceUp, w) } // allRedstoneDust returns a list of all redstone dust states. diff --git a/server/block/redstone_lamp.go b/server/block/redstone_lamp.go index e69becd4f..85af3665b 100644 --- a/server/block/redstone_lamp.go +++ b/server/block/redstone_lamp.go @@ -9,7 +9,7 @@ import ( type RedstoneLamp struct { solid - // Lit is if the redstone lamp is lit and disableEmitting light. + // Lit is if the redstone lamp is lit and emitting light. Lit bool } diff --git a/server/block/redstone_torch.go b/server/block/redstone_torch.go index c72a88bbd..99fa95ce5 100644 --- a/server/block/redstone_torch.go +++ b/server/block/redstone_torch.go @@ -14,7 +14,7 @@ type RedstoneTorch struct { // Facing is the direction from the torch to the block. Facing cube.Face - // Lit is if the redstone torch is lit and disableEmitting power. + // Lit is if the redstone torch is lit and emitting power. Lit bool } diff --git a/server/world/block.go b/server/world/block.go index da81744a1..072af1bb9 100644 --- a/server/world/block.go +++ b/server/world/block.go @@ -49,7 +49,6 @@ type Liquid interface { // Conductor represents a block that can conduct a redstone signal. type Conductor interface { Block - NeighbourUpdateTicker // WeakPower returns the power from a partial source and has limited usage. WeakPower(pos cube.Pos, face cube.Face, w *World) int // StrongPower returns the power from a full source and can be passed to any redstone component. diff --git a/server/world/world.go b/server/world/world.go index 3c9a16eb1..5792e9c3d 100644 --- a/server/world/world.go +++ b/server/world/world.go @@ -990,18 +990,17 @@ func (w *World) PortalDestination(dim Dimension) *World { } // EmittedRedstonePower returns the level of redstone power being emitted from a position to the provided face. -func (w *World) EmittedRedstonePower(pos cube.Pos, face cube.Face) int { - c, ok := w.Block(pos).(Conductor) - if !ok { - return 0 +func (w *World) EmittedRedstonePower(pos cube.Pos, face cube.Face) (weak int) { + b := w.Block(pos) + if c, ok := b.(Conductor); ok { + weak = c.WeakPower(pos, face, w) } - power := c.WeakPower(pos, face, w) for _, f := range cube.Faces() { - if !c.Model().FaceSolid(pos, f, w) { - return power + if !b.Model().FaceSolid(pos, f, w) { + return weak } } - return max(power, w.ReceivedStrongRedstonePower(pos)) + return max(weak, w.ReceivedStrongRedstonePower(pos)) } // ReceivedStrongRedstonePower returns the level of strong redstone power being received at the provided position. @@ -1011,7 +1010,7 @@ func (w *World) ReceivedStrongRedstonePower(pos cube.Pos) (power int) { if !ok { continue } - power = max(power, c.StrongPower(pos.Side(face), face.Opposite(), w)) + power = max(power, c.StrongPower(pos.Side(face), face, w)) if power >= 15 { return power } @@ -1027,9 +1026,9 @@ func (w *World) ReceivingRedstonePower(pos cube.Pos) bool { // ReceivedRedstonePower returns the highest level of redstone power received by the provided position. func (w *World) ReceivedRedstonePower(pos cube.Pos) (power int) { for _, face := range cube.Faces() { - emitted := w.EmittedRedstonePower(pos.Side(face), face.Opposite()) + emitted := w.EmittedRedstonePower(pos.Side(face), face) if emitted >= 15 { - return emitted + return 15 } power = max(emitted, power) } From 1aee785783cbd86dd0577289c3e77ffcd8992125 Mon Sep 17 00:00:00 2001 From: JustTal Date: Sun, 15 Jan 2023 23:27:05 -0600 Subject: [PATCH 07/83] block/redstone_dust.go: Fix disabled emitters check. --- server/block/redstone_dust.go | 32 ++++++-------------------------- 1 file changed, 6 insertions(+), 26 deletions(-) diff --git a/server/block/redstone_dust.go b/server/block/redstone_dust.go index 489c41e1a..38ae2d4d1 100644 --- a/server/block/redstone_dust.go +++ b/server/block/redstone_dust.go @@ -1,14 +1,11 @@ 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/sliceutil" "github.com/df-mc/dragonfly/server/item" "github.com/df-mc/dragonfly/server/world" "github.com/go-gl/mathgl/mgl64" - "golang.org/x/exp/slices" "sync" ) @@ -51,14 +48,6 @@ func (r RedstoneDust) NeighbourUpdateTick(pos, _ cube.Pos, w *world.World) { if r.Power != power { r.Power = power w.SetBlock(pos, r, nil) - for _, face := range cube.Faces() { - sidePos := pos.Side(face) - if c, ok := w.Block(sidePos).(world.Conductor); ok { - if n, ok := c.(world.NeighbourUpdateTicker); ok { - n.NeighbourUpdateTick(sidePos, pos, w) - } - } - } } } @@ -66,20 +55,17 @@ func (r RedstoneDust) NeighbourUpdateTick(pos, _ cube.Pos, w *world.World) { var disabledEmitters sync.Map // WeakPower ... -func (r RedstoneDust) WeakPower(pos cube.Pos, side cube.Face, w *world.World) int { - if _, ok := disabledEmitters.Load(pos); ok || side == cube.FaceDown { +func (r RedstoneDust) WeakPower(pos cube.Pos, face cube.Face, w *world.World) int { + if _, ok := disabledEmitters.Load(pos.Side(face.Opposite())); ok { return 0 } - if side == cube.FaceUp { - return r.Power + if face == cube.FaceDown { + return 0 } - faces := sliceutil.Filter(cube.HorizontalFaces(), func(face cube.Face) bool { - return r.connection(pos, face, w) - }) - if len(faces) == 0 { + if face == cube.FaceUp { return r.Power } - if slices.Contains(faces, side) && !slices.Contains(faces, side.RotateLeft()) && !slices.Contains(faces, side.RotateRight()) { + if r.connection(pos, face, w) && !r.connection(pos, face.RotateLeft(), w) && !r.connection(pos, face.RotateRight(), w) { return r.Power } return 0 @@ -87,9 +73,6 @@ func (r RedstoneDust) WeakPower(pos cube.Pos, side cube.Face, w *world.World) in // StrongPower ... func (r RedstoneDust) StrongPower(pos cube.Pos, face cube.Face, w *world.World) int { - if _, ok := disabledEmitters.Load(pos); ok { - return 0 - } return r.WeakPower(pos, face, w) } @@ -112,9 +95,6 @@ func (r RedstoneDust) receivedPower(pos cube.Pos, w *world.World) int { } } } - fmt.Printf("Received: %v\n", received) - fmt.Printf("Power: %v\n", power-1) - fmt.Println(max(received, power-1)) return max(received, power-1) } From 9004e990b4651ea2c9e9a6f8d27ad267621ba1c2 Mon Sep 17 00:00:00 2001 From: JustTal Date: Mon, 16 Jan 2023 19:41:00 -0600 Subject: [PATCH 08/83] block/redstone.go: Started implementation of non-locational redstone wires. (based off of RedstoneWireTurbo and MCHPRS) --- server/block/redstone.go | 311 +++++++++++++++++++++++++++++++++++++++ server/world/block.go | 4 +- server/world/world.go | 36 +---- 3 files changed, 321 insertions(+), 30 deletions(-) create mode 100644 server/block/redstone.go diff --git a/server/block/redstone.go b/server/block/redstone.go new file mode 100644 index 000000000..c5286f875 --- /dev/null +++ b/server/block/redstone.go @@ -0,0 +1,311 @@ +package block + +import ( + "github.com/df-mc/dragonfly/server/block/cube" + "github.com/df-mc/dragonfly/server/block/model" + "github.com/df-mc/dragonfly/server/world" +) + +type wireNode struct { + visited bool + + pos cube.Pos + block world.Block + + neighbours []*wireNode + oriented bool + + xBias int32 + zBias int32 + + layer uint32 +} + +type wireNetwork struct { + nodes []*wireNode + nodeCache map[cube.Pos]*wireNode + updateQueue [3][]*wireNode + currentWalkLayer uint32 +} + +const ( + wireHeadingNorth = 0 + wireHeadingEast = 1 + wireHeadingSouth = 2 + wireHeadingWest = 3 +) + +func updateSurroundingRedstone(w *world.World, pos cube.Pos) { + n := &wireNetwork{ + nodeCache: make(map[cube.Pos]*wireNode), + updateQueue: [3][]*wireNode{}, + } + + root := &wireNode{ + block: w.Block(pos), + pos: pos, + visited: true, + } + n.nodeCache[pos] = root + n.nodes = append(n.nodes, root) + + n.propagateChanges(w, root, 0) + n.breadthFirstWalk(w) +} + +func (n *wireNetwork) identifyNeighbours(w *world.World, node *wireNode) { + neighbours := computeRedstoneNeighbours(node.pos) + neighboursVisited := make([]bool, 0, 24) + neighbourNodes := make([]*wireNode, 0, 24) + for _, neighbourPos := range neighbours[:24] { + neighbour, ok := n.nodeCache[neighbourPos] + if !ok { + neighbour = &wireNode{ + pos: neighbourPos, + block: w.Block(neighbourPos), + } + n.nodeCache[neighbourPos] = neighbour + n.nodes = append(n.nodes, neighbour) + } + + neighbourNodes = append(neighbourNodes, neighbour) + neighboursVisited = append(neighboursVisited, neighbour.visited) + } + + fromWest := neighboursVisited[0] || neighboursVisited[7] || neighboursVisited[8] + fromEast := neighboursVisited[1] || neighboursVisited[12] || neighboursVisited[13] + fromNorth := neighboursVisited[4] || neighboursVisited[17] || neighboursVisited[20] + fromSouth := neighboursVisited[5] || neighboursVisited[18] || neighboursVisited[21] + + var cX, cZ int32 + if fromWest { + cX++ + } + if fromEast { + cX-- + } + if fromNorth { + cZ++ + } + if fromSouth { + cZ-- + } + + var heading uint32 + if cX == 0 && cZ == 0 { + heading = computeRedstoneHeading(node.xBias, node.zBias) + for _, neighbourNode := range neighbourNodes { + neighbourNode.xBias = node.xBias + neighbourNode.zBias = node.zBias + } + } else { + if cX != 0 && cZ != 0 { + if node.xBias != 0 { + cZ = 0 + } + if node.zBias != 0 { + cX = 0 + } + } + heading = computeRedstoneHeading(cX, cZ) + for _, neighbourNode := range neighbourNodes { + neighbourNode.xBias = cX + neighbourNode.zBias = cZ + } + } + + n.orientNeighbours(&neighbourNodes, node, heading) +} + +var reordering = [...][...]uint32{ + {2, 3, 16, 19, 0, 4, 1, 5, 7, 8, 17, 20, 12, 13, 18, 21, 6, 9, 22, 14, 11, 10, 23, 15}, + {2, 3, 16, 19, 4, 1, 5, 0, 17, 20, 12, 13, 18, 21, 7, 8, 22, 14, 11, 15, 23, 9, 6, 10}, + {2, 3, 16, 19, 1, 5, 0, 4, 12, 13, 18, 21, 7, 8, 17, 20, 11, 15, 23, 10, 6, 14, 22, 9}, + {2, 3, 16, 19, 5, 0, 4, 1, 18, 21, 7, 8, 17, 20, 12, 13, 23, 10, 6, 9, 22, 15, 11, 14}, +} + +func (n *wireNetwork) orientNeighbours(src *[]*wireNode, dst *wireNode, heading uint32) { + dst.oriented = true + dst.neighbours = make([]*wireNode, 0, 24) + for _, i := range reordering[heading] { + dst.neighbours = append(dst.neighbours, (*src)[i]) + } +} + +func (n *wireNetwork) propagateChanges(w *world.World, node *wireNode, layer uint32) { + if !node.oriented { + n.identifyNeighbours(w, node) + } + + layerOne := layer + 1 + for _, neighbour := range node.neighbours[:24] { + if layerOne > neighbour.layer { + neighbour.layer = layerOne + n.updateQueue[1] = append(n.updateQueue[1], neighbour) + } + } + + layerTwo := layer + 2 + for _, neighbour := range node.neighbours[:4] { + if layerTwo > neighbour.layer { + neighbour.layer = layerTwo + n.updateQueue[2] = append(n.updateQueue[2], neighbour) + } + } +} + +func (n *wireNetwork) breadthFirstWalk(w *world.World) { + n.shiftQueue() + n.currentWalkLayer = 1 + + for len(n.updateQueue[0]) > 0 || len(n.updateQueue[1]) > 0 { + for _, node := range n.updateQueue[0] { + if _, ok := node.block.(RedstoneWire); ok { + n.updateNode(w, node, n.currentWalkLayer) + continue + } + // TODO: Send a regular block update. + } + + n.shiftQueue() + n.currentWalkLayer++ + } + + n.currentWalkLayer = 0 +} + +func (n *wireNetwork) shiftQueue() { + n.updateQueue[0] = n.updateQueue[1] + n.updateQueue[1] = n.updateQueue[2] + n.updateQueue[2] = nil +} + +func (n *wireNetwork) updateNode(w *world.World, node *wireNode, layer uint32) { + node.visited = true + + oldWire := node.block.(RedstoneWire) + newWire := n.calculateCurrentChanges(w, node) + if oldWire.Power != newWire.Power { + node.block = newWire + + n.propagateChanges(w, node, layer) + } +} + +var ( + rsNeighbours = [...]uint32{4, 5, 6, 7} + rsNeighboursUp = [...]uint32{9, 11, 13, 15} + rsNeighboursDn = [...]uint32{8, 10, 12, 14} +) + +func (n *wireNetwork) calculateCurrentChanges(w *world.World, node *wireNode) RedstoneWire { + wire := node.block.(RedstoneWire) + i := wire.Power + + var blockPower int + if !node.oriented { + n.identifyNeighbours(w, node) + } + + var wirePower int + for _, face := range cube.Faces() { + wirePower = max(wirePower, w.EmittedRedstonePower(node.pos.Side(face), face, false)) + } + + if wirePower < 15 { + centerUp := node.neighbours[1].block + _, centerUpSolid := centerUp.Model().(model.Solid) + for m := 0; m < 4; m++ { + neighbour := node.neighbours[rsNeighbours[m]].block + _, neighbourSolid := neighbour.Model().(model.Solid) + + blockPower = n.maxCurrentStrength(neighbour, blockPower) + if !neighbourSolid { + neighbourDown := node.neighbours[rsNeighboursDn[m]].block + blockPower = n.maxCurrentStrength(neighbourDown, blockPower) + } else if d, ok := neighbour.(LightDiffuser); (!ok || d.LightDiffusionLevel() > 0) && !centerUpSolid { + neighbourUp := node.neighbours[rsNeighboursUp[m]].block + blockPower = n.maxCurrentStrength(neighbourUp, blockPower) + } + } + } + + j := blockPower - 1 + if wirePower > j { + j = wirePower + } + + if i != j { + wire.Power = j + w.SetBlock(node.pos, wire, &world.SetOpts{DisableBlockUpdates: true}) + } + return wire +} + +func (n *wireNetwork) maxCurrentStrength(neighbour world.Block, strength int) int { + if wire, ok := neighbour.(RedstoneWire); ok { + return max(wire.Power, strength) + } + return strength +} + +func computeRedstoneNeighbours(pos cube.Pos) []cube.Pos { + return []cube.Pos{ + // Immediate neighbours, in the order of west, east, down, up, north, and finally south. + pos.Side(cube.FaceWest), + pos.Side(cube.FaceEast), + pos.Side(cube.FaceDown), + pos.Side(cube.FaceUp), + pos.Side(cube.FaceNorth), + pos.Side(cube.FaceSouth), + + // Neighbours of neighbours, in the same order, except that duplicates are not included. + pos.Side(cube.FaceWest).Side(cube.FaceWest), + pos.Side(cube.FaceWest).Side(cube.FaceDown), + pos.Side(cube.FaceWest).Side(cube.FaceUp), + pos.Side(cube.FaceWest).Side(cube.FaceNorth), + pos.Side(cube.FaceWest).Side(cube.FaceSouth), + + pos.Side(cube.FaceEast).Side(cube.FaceEast), + pos.Side(cube.FaceEast).Side(cube.FaceDown), + pos.Side(cube.FaceEast).Side(cube.FaceUp), + pos.Side(cube.FaceEast).Side(cube.FaceNorth), + pos.Side(cube.FaceEast).Side(cube.FaceSouth), + + pos.Side(cube.FaceDown).Side(cube.FaceDown), + pos.Side(cube.FaceDown).Side(cube.FaceNorth), + pos.Side(cube.FaceDown).Side(cube.FaceSouth), + + pos.Side(cube.FaceUp).Side(cube.FaceUp), + pos.Side(cube.FaceUp).Side(cube.FaceNorth), + pos.Side(cube.FaceUp).Side(cube.FaceSouth), + + pos.Side(cube.FaceNorth).Side(cube.FaceNorth), + pos.Side(cube.FaceSouth).Side(cube.FaceSouth), + } +} + +func computeRedstoneHeading(rX, rZ int32) uint32 { + code := (rX + 1) + 3*(rZ+1) + switch code { + case 0: + return wireHeadingNorth + case 1: + return wireHeadingNorth + case 2: + return wireHeadingEast + case 3: + return wireHeadingWest + case 4: + return wireHeadingWest + case 5: + return wireHeadingEast + case 6: + return wireHeadingSouth + case 7: + return wireHeadingSouth + case 8: + return wireHeadingSouth + } + panic("should never happen") +} diff --git a/server/world/block.go b/server/world/block.go index 15abc2bd7..b5d9bb021 100644 --- a/server/world/block.go +++ b/server/world/block.go @@ -53,9 +53,9 @@ type Liquid interface { type Conductor interface { Block // WeakPower returns the power from a partial source and has limited usage. - WeakPower(pos cube.Pos, face cube.Face, w *World) int + WeakPower(pos cube.Pos, face cube.Face, w *World, dustPower bool) int // StrongPower returns the power from a full source and can be passed to any redstone component. - StrongPower(pos cube.Pos, face cube.Face, w *World) int + StrongPower(pos cube.Pos, face cube.Face, w *World, dustPower bool) int } // hashes holds a list of runtime IDs indexed by the hash of the Block that implements the blocks pointed to by those diff --git a/server/world/world.go b/server/world/world.go index 89891e22b..f594aa364 100644 --- a/server/world/world.go +++ b/server/world/world.go @@ -1004,47 +1004,27 @@ func (w *World) PortalDestination(dim Dimension) *World { } // EmittedRedstonePower returns the level of redstone power being emitted from a position to the provided face. -func (w *World) EmittedRedstonePower(pos cube.Pos, face cube.Face) (weak int) { +func (w *World) EmittedRedstonePower(pos cube.Pos, face cube.Face, dustPower bool) int { b := w.Block(pos) - if c, ok := b.(Conductor); ok { - weak = c.WeakPower(pos, face, w) - } for _, f := range cube.Faces() { if !b.Model().FaceSolid(pos, f, w) { - return weak + if c, ok := w.Block(pos).(Conductor); ok { + return c.WeakPower(pos, face, w, dustPower) + } + return 0 } } - return max(weak, w.ReceivedStrongRedstonePower(pos)) + return w.ReceivedStrongRedstonePower(pos, dustPower) } // ReceivedStrongRedstonePower returns the level of strong redstone power being received at the provided position. -func (w *World) ReceivedStrongRedstonePower(pos cube.Pos) (power int) { +func (w *World) ReceivedStrongRedstonePower(pos cube.Pos, dustPower bool) (power int) { for _, face := range cube.Faces() { c, ok := w.Block(pos.Side(face)).(Conductor) if !ok { continue } - power = max(power, c.StrongPower(pos.Side(face), face, w)) - if power >= 15 { - return power - } - } - return power -} - -// ReceivingRedstonePower returns if the provided position is receiving any level of redstone power. -func (w *World) ReceivingRedstonePower(pos cube.Pos) bool { - return w.ReceivedRedstonePower(pos) > 0 -} - -// ReceivedRedstonePower returns the highest level of redstone power received by the provided position. -func (w *World) ReceivedRedstonePower(pos cube.Pos) (power int) { - for _, face := range cube.Faces() { - emitted := w.EmittedRedstonePower(pos.Side(face), face) - if emitted >= 15 { - return 15 - } - power = max(emitted, power) + power = max(power, c.StrongPower(pos.Side(face), face, w, dustPower)) } return power } From bfa71909c6207dd94c531338e30c509fbf8c745b Mon Sep 17 00:00:00 2001 From: JustTal Date: Mon, 16 Jan 2023 20:25:33 -0600 Subject: [PATCH 09/83] block/redstone_wire.go: Various fixes. --- main.go | 8 +- server/block/hash.go | 2 +- server/block/redstone.go | 4 +- server/block/redstone_block.go | 4 +- server/block/redstone_lamp.go | 14 ++- server/block/redstone_torch.go | 4 +- .../{redstone_dust.go => redstone_wire.go} | 90 ++++++++++--------- server/block/register.go | 2 +- server/world/block.go | 4 +- server/world/world.go | 16 ++-- 10 files changed, 78 insertions(+), 70 deletions(-) rename server/block/{redstone_dust.go => redstone_wire.go} (50%) diff --git a/main.go b/main.go index 4043c000d..0c7b80258 100644 --- a/main.go +++ b/main.go @@ -6,7 +6,6 @@ import ( "github.com/df-mc/dragonfly/server/block" "github.com/df-mc/dragonfly/server/block/cube" "github.com/df-mc/dragonfly/server/block/cube/trace" - "github.com/df-mc/dragonfly/server/entity" "github.com/df-mc/dragonfly/server/player" "github.com/df-mc/dragonfly/server/player/chat" "github.com/df-mc/dragonfly/server/world" @@ -89,14 +88,12 @@ func (h *redstonePlayerHandler) tick() { case <-t.C: w := h.p.World() start := h.p.Position().Add(mgl64.Vec3{0, h.p.EyeHeight()}) - end := start.Add(entity.DirectionVector(h.p).Mul(50)) + end := start.Add(h.p.Rotation().Vec3().Mul(50)) var hitBlock world.Block - var hitPos cube.Pos trace.TraverseBlocks(start, end, func(pos cube.Pos) bool { b := w.Block(pos) if _, ok := b.(block.Air); !ok { hitBlock = b - hitPos = pos return false } return true @@ -104,10 +101,9 @@ func (h *redstonePlayerHandler) tick() { if hitBlock != nil { popup := fmt.Sprintf("%T", hitBlock) switch hitBlock := hitBlock.(type) { - case block.RedstoneDust: + case block.RedstoneWire: popup += fmt.Sprintf("\nPower: %d", hitBlock.Power) } - popup += fmt.Sprintf("\nCalculated Power: %d", w.ReceivedRedstonePower(hitPos)) h.p.SendPopup(popup) } else { h.p.SendPopup("You are not looking at a block") diff --git a/server/block/hash.go b/server/block/hash.go index 7d1b54f6e..6cfa5aa12 100644 --- a/server/block/hash.go +++ b/server/block/hash.go @@ -686,7 +686,7 @@ func (RedstoneBlock) Hash() uint64 { return hashRedstoneBlock } -func (r RedstoneDust) Hash() uint64 { +func (r RedstoneWire) Hash() uint64 { return hashRedstoneDust | uint64(r.Power)<<8 } diff --git a/server/block/redstone.go b/server/block/redstone.go index c5286f875..f2ebde928 100644 --- a/server/block/redstone.go +++ b/server/block/redstone.go @@ -35,7 +35,7 @@ const ( wireHeadingWest = 3 ) -func updateSurroundingRedstone(w *world.World, pos cube.Pos) { +func updateSurroundingRedstone(pos cube.Pos, w *world.World) { n := &wireNetwork{ nodeCache: make(map[cube.Pos]*wireNode), updateQueue: [3][]*wireNode{}, @@ -117,7 +117,7 @@ func (n *wireNetwork) identifyNeighbours(w *world.World, node *wireNode) { n.orientNeighbours(&neighbourNodes, node, heading) } -var reordering = [...][...]uint32{ +var reordering = [][]uint32{ {2, 3, 16, 19, 0, 4, 1, 5, 7, 8, 17, 20, 12, 13, 18, 21, 6, 9, 22, 14, 11, 10, 23, 15}, {2, 3, 16, 19, 4, 1, 5, 0, 17, 20, 12, 13, 18, 21, 7, 8, 22, 14, 11, 15, 23, 9, 6, 10}, {2, 3, 16, 19, 1, 5, 0, 4, 12, 13, 18, 21, 7, 8, 17, 20, 11, 15, 23, 10, 6, 14, 22, 9}, diff --git a/server/block/redstone_block.go b/server/block/redstone_block.go index 2379d4f1b..5da9d2843 100644 --- a/server/block/redstone_block.go +++ b/server/block/redstone_block.go @@ -18,11 +18,11 @@ func (b RedstoneBlock) EncodeBlock() (string, map[string]any) { } // WeakPower ... -func (b RedstoneBlock) WeakPower(cube.Pos, cube.Face, *world.World) int { +func (b RedstoneBlock) WeakPower(cube.Pos, cube.Face, *world.World, bool) int { return 15 } // StrongPower ... -func (b RedstoneBlock) StrongPower(cube.Pos, cube.Face, *world.World) int { +func (b RedstoneBlock) StrongPower(cube.Pos, cube.Face, *world.World, bool) int { return 0 } diff --git a/server/block/redstone_lamp.go b/server/block/redstone_lamp.go index 85af3665b..620ef5a36 100644 --- a/server/block/redstone_lamp.go +++ b/server/block/redstone_lamp.go @@ -45,15 +45,25 @@ func (l RedstoneLamp) UseOnBlock(pos cube.Pos, face cube.Face, _ mgl64.Vec3, w * if !used { return } - l.Lit = w.ReceivingRedstonePower(pos) + l.Lit = l.receiveRedstonePower(pos, w) place(w, pos, l, user, ctx) return placed(ctx) } // NeighbourUpdateTick ... func (l RedstoneLamp) NeighbourUpdateTick(pos, _ cube.Pos, w *world.World) { - if l.Lit != w.ReceivingRedstonePower(pos) { + if l.Lit != l.receiveRedstonePower(pos, w) { l.Lit = !l.Lit w.SetBlock(pos, l, nil) } } + +// receiveRedstonePower ... +func (l RedstoneLamp) receiveRedstonePower(pos cube.Pos, w *world.World) bool { + for _, face := range cube.Faces() { + if w.EmittedRedstonePower(pos.Side(face), face, true) > 0 { + return true + } + } + return false +} diff --git a/server/block/redstone_torch.go b/server/block/redstone_torch.go index 99fa95ce5..3d22b4996 100644 --- a/server/block/redstone_torch.go +++ b/server/block/redstone_torch.go @@ -93,7 +93,7 @@ func (t RedstoneTorch) EncodeBlock() (name string, properties map[string]any) { } // WeakPower ... -func (t RedstoneTorch) WeakPower(_ cube.Pos, face cube.Face, _ *world.World) int { +func (t RedstoneTorch) WeakPower(_ cube.Pos, face cube.Face, _ *world.World, _ bool) int { if t.Lit && face != cube.FaceUp { return 15 } @@ -101,7 +101,7 @@ func (t RedstoneTorch) WeakPower(_ cube.Pos, face cube.Face, _ *world.World) int } // StrongPower ... -func (t RedstoneTorch) StrongPower(cube.Pos, cube.Face, *world.World) int { +func (t RedstoneTorch) StrongPower(cube.Pos, cube.Face, *world.World, bool) int { return 0 } diff --git a/server/block/redstone_dust.go b/server/block/redstone_wire.go similarity index 50% rename from server/block/redstone_dust.go rename to server/block/redstone_wire.go index 38ae2d4d1..22e136603 100644 --- a/server/block/redstone_dust.go +++ b/server/block/redstone_wire.go @@ -6,29 +6,28 @@ import ( "github.com/df-mc/dragonfly/server/item" "github.com/df-mc/dragonfly/server/world" "github.com/go-gl/mathgl/mgl64" - "sync" ) -type RedstoneDust struct { +type RedstoneWire struct { empty transparent Power int } // EncodeItem ... -func (r RedstoneDust) EncodeItem() (name string, meta int16) { +func (r RedstoneWire) EncodeItem() (name string, meta int16) { return "minecraft:redstone", 0 } // EncodeBlock ... -func (r RedstoneDust) EncodeBlock() (string, map[string]any) { +func (r RedstoneWire) EncodeBlock() (string, map[string]any) { return "minecraft:redstone_wire", map[string]any{ "redstone_signal": int32(r.Power), } } // UseOnBlock ... -func (r RedstoneDust) UseOnBlock(pos cube.Pos, face cube.Face, _ mgl64.Vec3, w *world.World, user item.User, ctx *item.UseContext) (used bool) { +func (r RedstoneWire) 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, r) if !used { return @@ -37,26 +36,24 @@ func (r RedstoneDust) UseOnBlock(pos cube.Pos, face cube.Face, _ mgl64.Vec3, w * if !w.Block(belowPos).Model().FaceSolid(belowPos, cube.FaceUp, w) { return } - r.Power = r.receivedPower(pos, w) + r.Power = r.calculatePower(pos, w) place(w, pos, r, user, ctx) return placed(ctx) } // NeighbourUpdateTick ... -func (r RedstoneDust) NeighbourUpdateTick(pos, _ cube.Pos, w *world.World) { - power := r.receivedPower(pos, w) +func (r RedstoneWire) NeighbourUpdateTick(pos, _ cube.Pos, w *world.World) { + power := r.calculatePower(pos, w) if r.Power != power { r.Power = power w.SetBlock(pos, r, nil) + updateSurroundingRedstone(pos, w) } } -// disabledEmitters ... -var disabledEmitters sync.Map - // WeakPower ... -func (r RedstoneDust) WeakPower(pos cube.Pos, face cube.Face, w *world.World) int { - if _, ok := disabledEmitters.Load(pos.Side(face.Opposite())); ok { +func (r RedstoneWire) WeakPower(pos cube.Pos, face cube.Face, w *world.World, includeDust bool) int { + if !includeDust { return 0 } if face == cube.FaceDown { @@ -72,43 +69,48 @@ func (r RedstoneDust) WeakPower(pos cube.Pos, face cube.Face, w *world.World) in } // StrongPower ... -func (r RedstoneDust) StrongPower(pos cube.Pos, face cube.Face, w *world.World) int { - return r.WeakPower(pos, face, w) +func (r RedstoneWire) StrongPower(pos cube.Pos, face cube.Face, w *world.World, includeDust bool) int { + return r.WeakPower(pos, face, w, includeDust) } -// receivedPower returns the highest level of received redstone power at the provided position. -func (r RedstoneDust) receivedPower(pos cube.Pos, w *world.World) int { - disabledEmitters.Store(pos, struct{}{}) - received := w.ReceivedRedstonePower(pos) - disabledEmitters.Delete(pos) - - var power int - if received < 15 { - _, solidAbove := w.Block(pos.Side(cube.FaceUp)).Model().(model.Solid) - for _, face := range cube.HorizontalFaces() { - sidePos := pos.Side(face) - power = max(power, r.checkPower(sidePos, w)) - if _, sideSolid := w.Block(sidePos).Model().(model.Solid); sideSolid && !solidAbove { - power = max(power, r.checkPower(sidePos.Side(cube.FaceUp), w)) - } else if !sideSolid { - power = max(power, r.checkPower(sidePos.Side(cube.FaceDown), w)) - } +// calculatePower returns the highest level of received redstone power at the provided position. +func (r RedstoneWire) calculatePower(pos cube.Pos, w *world.World) int { + aboveBlock := w.Block(pos.Side(cube.FaceUp)) + _, aboveSolid := aboveBlock.Model().(model.Solid) + + var blockPower, wirePower int + for _, side := range cube.Faces() { + neighbourPos := pos.Side(side) + neighbour := w.Block(neighbourPos) + + wirePower = r.maxCurrentStrength(wirePower, neighbourPos, w) + blockPower = max(blockPower, w.EmittedRedstonePower(neighbourPos, side, false)) + + if side.Axis() == cube.Y { + // Only check horizontal neighbours from here on. + continue + } + + if d, ok := neighbour.(LightDiffuser); (!ok || d.LightDiffusionLevel() > 0) && !aboveSolid { + wirePower = r.maxCurrentStrength(wirePower, neighbourPos.Side(cube.FaceUp), w) + } + if _, neighbourSolid := neighbour.Model().(model.Solid); !neighbourSolid { + wirePower = r.maxCurrentStrength(wirePower, neighbourPos.Side(cube.FaceDown), w) } } - return max(received, power-1) + return max(blockPower, wirePower-1) } -// checkPower attempts to return the power level of the redstone dust at the provided position if it exists. If there is -// no redstone dust at the position, 0 is returned. -func (r RedstoneDust) checkPower(pos cube.Pos, w *world.World) int { - if b, ok := w.Block(pos).(RedstoneDust); ok { - return b.Power +// maxCurrentStrength ... +func (r RedstoneWire) maxCurrentStrength(power int, pos cube.Pos, w *world.World) int { + if wire, ok := w.Block(pos).(RedstoneWire); ok { + return max(wire.Power, power) } - return 0 + return power } // connection returns true if the dust connects to the given face. -func (r RedstoneDust) connection(pos cube.Pos, face cube.Face, w *world.World) bool { +func (r RedstoneWire) connection(pos cube.Pos, face cube.Face, w *world.World) bool { sidePos := pos.Side(face) sideBlock := w.Block(sidePos) if _, solidAbove := w.Block(pos.Side(cube.FaceUp)).Model().(model.Solid); !solidAbove && r.canRunOnTop(w, sidePos, sideBlock) && r.connectsTo(w.Block(sidePos.Side(cube.FaceUp)), false) { @@ -119,9 +121,9 @@ func (r RedstoneDust) connection(pos cube.Pos, face cube.Face, w *world.World) b } // connectsTo ... -func (r RedstoneDust) connectsTo(block world.Block, hasFace bool) bool { +func (r RedstoneWire) connectsTo(block world.Block, hasFace bool) bool { switch block.(type) { - case RedstoneDust: + case RedstoneWire: return true // TODO: Repeaters, observers } @@ -132,7 +134,7 @@ func (r RedstoneDust) connectsTo(block world.Block, hasFace bool) bool { } // canRunOnTop ... -func (r RedstoneDust) canRunOnTop(w *world.World, pos cube.Pos, block world.Block) bool { +func (r RedstoneWire) canRunOnTop(w *world.World, pos cube.Pos, block world.Block) bool { // TODO: Hoppers. return block.Model().FaceSolid(pos, cube.FaceUp, w) } @@ -140,7 +142,7 @@ func (r RedstoneDust) canRunOnTop(w *world.World, pos cube.Pos, block world.Bloc // allRedstoneDust returns a list of all redstone dust states. func allRedstoneDust() (all []world.Block) { for i := 0; i < 16; i++ { - all = append(all, RedstoneDust{Power: i}) + all = append(all, RedstoneWire{Power: i}) } return } diff --git a/server/block/register.go b/server/block/register.go index ce545f98c..066a8b540 100644 --- a/server/block/register.go +++ b/server/block/register.go @@ -308,7 +308,7 @@ func init() { world.RegisterItem(RawGold{}) world.RegisterItem(RawIron{}) world.RegisterItem(RedstoneBlock{}) - world.RegisterItem(RedstoneDust{}) + world.RegisterItem(RedstoneWire{}) world.RegisterItem(RedstoneTorch{}) world.RegisterItem(ReinforcedDeepslate{}) world.RegisterItem(Sand{Red: true}) diff --git a/server/world/block.go b/server/world/block.go index b5d9bb021..c36565b61 100644 --- a/server/world/block.go +++ b/server/world/block.go @@ -53,9 +53,9 @@ type Liquid interface { type Conductor interface { Block // WeakPower returns the power from a partial source and has limited usage. - WeakPower(pos cube.Pos, face cube.Face, w *World, dustPower bool) int + WeakPower(pos cube.Pos, face cube.Face, w *World, includeDust bool) int // StrongPower returns the power from a full source and can be passed to any redstone component. - StrongPower(pos cube.Pos, face cube.Face, w *World, dustPower bool) int + StrongPower(pos cube.Pos, face cube.Face, w *World, includeDust bool) int } // hashes holds a list of runtime IDs indexed by the hash of the Block that implements the blocks pointed to by those diff --git a/server/world/world.go b/server/world/world.go index f594aa364..095a6de64 100644 --- a/server/world/world.go +++ b/server/world/world.go @@ -1004,27 +1004,27 @@ func (w *World) PortalDestination(dim Dimension) *World { } // EmittedRedstonePower returns the level of redstone power being emitted from a position to the provided face. -func (w *World) EmittedRedstonePower(pos cube.Pos, face cube.Face, dustPower bool) int { +func (w *World) EmittedRedstonePower(pos cube.Pos, face cube.Face, includeDust bool) (emitted int) { b := w.Block(pos) + if c, ok := b.(Conductor); ok { + emitted = c.WeakPower(pos, face, w, includeDust) + } for _, f := range cube.Faces() { if !b.Model().FaceSolid(pos, f, w) { - if c, ok := w.Block(pos).(Conductor); ok { - return c.WeakPower(pos, face, w, dustPower) - } - return 0 + return emitted } } - return w.ReceivedStrongRedstonePower(pos, dustPower) + return max(emitted, w.ReceivedStrongRedstonePower(pos, includeDust)) } // ReceivedStrongRedstonePower returns the level of strong redstone power being received at the provided position. -func (w *World) ReceivedStrongRedstonePower(pos cube.Pos, dustPower bool) (power int) { +func (w *World) ReceivedStrongRedstonePower(pos cube.Pos, includeDust bool) (power int) { for _, face := range cube.Faces() { c, ok := w.Block(pos.Side(face)).(Conductor) if !ok { continue } - power = max(power, c.StrongPower(pos.Side(face), face, w, dustPower)) + power = max(power, c.StrongPower(pos.Side(face), face, w, includeDust)) } return power } From 62c68407819e3ce9dc564d5b18d3ab0e5e1fcb68 Mon Sep 17 00:00:00 2001 From: JustTal Date: Mon, 16 Jan 2023 20:28:44 -0600 Subject: [PATCH 10/83] block/redstone_wire.go: Avoid unnecessary block updates. --- server/block/redstone_wire.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/block/redstone_wire.go b/server/block/redstone_wire.go index 22e136603..7f7ce8c73 100644 --- a/server/block/redstone_wire.go +++ b/server/block/redstone_wire.go @@ -46,7 +46,7 @@ func (r RedstoneWire) NeighbourUpdateTick(pos, _ cube.Pos, w *world.World) { power := r.calculatePower(pos, w) if r.Power != power { r.Power = power - w.SetBlock(pos, r, nil) + w.SetBlock(pos, r, &world.SetOpts{DisableBlockUpdates: true}) updateSurroundingRedstone(pos, w) } } From d1f2c6a4ec3cd36220012a200e22e5bf2f24111f Mon Sep 17 00:00:00 2001 From: JustTal Date: Mon, 16 Jan 2023 20:51:52 -0600 Subject: [PATCH 11/83] block/redstone_lamp.go: type lol --- server/block/redstone_lamp.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/server/block/redstone_lamp.go b/server/block/redstone_lamp.go index 620ef5a36..e8415bc05 100644 --- a/server/block/redstone_lamp.go +++ b/server/block/redstone_lamp.go @@ -45,21 +45,21 @@ func (l RedstoneLamp) UseOnBlock(pos cube.Pos, face cube.Face, _ mgl64.Vec3, w * if !used { return } - l.Lit = l.receiveRedstonePower(pos, w) + l.Lit = l.receivedRedstonePower(pos, w) place(w, pos, l, user, ctx) return placed(ctx) } // NeighbourUpdateTick ... func (l RedstoneLamp) NeighbourUpdateTick(pos, _ cube.Pos, w *world.World) { - if l.Lit != l.receiveRedstonePower(pos, w) { + if l.Lit != l.receivedRedstonePower(pos, w) { l.Lit = !l.Lit w.SetBlock(pos, l, nil) } } -// receiveRedstonePower ... -func (l RedstoneLamp) receiveRedstonePower(pos cube.Pos, w *world.World) bool { +// receivedRedstonePower ... +func (l RedstoneLamp) receivedRedstonePower(pos cube.Pos, w *world.World) bool { for _, face := range cube.Faces() { if w.EmittedRedstonePower(pos.Side(face), face, true) > 0 { return true From e480b614188c386e244c1d15a4ac95285d24825e Mon Sep 17 00:00:00 2001 From: JustTal Date: Mon, 16 Jan 2023 20:56:03 -0600 Subject: [PATCH 12/83] block/redstone_wire.go: Also update surrounding redstone on break. --- server/block/redstone_wire.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/server/block/redstone_wire.go b/server/block/redstone_wire.go index 7f7ce8c73..1cdacb5e0 100644 --- a/server/block/redstone_wire.go +++ b/server/block/redstone_wire.go @@ -14,6 +14,13 @@ type RedstoneWire struct { Power int } +// BreakInfo ... +func (r RedstoneWire) BreakInfo() BreakInfo { + return newBreakInfo(0, alwaysHarvestable, nothingEffective, oneOf(r)).withBreakHandler(func(pos cube.Pos, w *world.World, _ item.User) { + updateSurroundingRedstone(pos, w) + }) +} + // EncodeItem ... func (r RedstoneWire) EncodeItem() (name string, meta int16) { return "minecraft:redstone", 0 From 8357d6304ab144e098573fbfc063d300609ed0c0 Mon Sep 17 00:00:00 2001 From: JustTal Date: Mon, 16 Jan 2023 20:59:10 -0600 Subject: [PATCH 13/83] block/redstone_wire.go: And on place! --- server/block/redstone_wire.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/server/block/redstone_wire.go b/server/block/redstone_wire.go index 1cdacb5e0..f5deadbc1 100644 --- a/server/block/redstone_wire.go +++ b/server/block/redstone_wire.go @@ -45,7 +45,11 @@ func (r RedstoneWire) UseOnBlock(pos cube.Pos, face cube.Face, _ mgl64.Vec3, w * } r.Power = r.calculatePower(pos, w) place(w, pos, r, user, ctx) - return placed(ctx) + if placed(ctx) { + updateSurroundingRedstone(pos, w) + return true + } + return false } // NeighbourUpdateTick ... From 5f33b8aa759f9c04f53e91fc576d796aa6d708fb Mon Sep 17 00:00:00 2001 From: JustTal Date: Tue, 17 Jan 2023 15:47:24 -0600 Subject: [PATCH 14/83] block: Documented redstone related components. --- main.go | 2 ++ server/block/diode.go | 1 + server/block/hash.go | 10 +++---- server/block/redstone.go | 50 ++++++++++++++++++++++++++++------ server/block/redstone_block.go | 5 ++++ server/block/redstone_lamp.go | 4 +-- server/block/redstone_torch.go | 15 ++++++++-- server/block/redstone_wire.go | 15 ++++++++-- server/block/register.go | 2 +- server/world/block.go | 2 ++ server/world/item.go | 1 + 11 files changed, 87 insertions(+), 20 deletions(-) create mode 100644 server/block/diode.go diff --git a/main.go b/main.go index 0c7b80258..49dadcd9b 100644 --- a/main.go +++ b/main.go @@ -6,6 +6,7 @@ import ( "github.com/df-mc/dragonfly/server/block" "github.com/df-mc/dragonfly/server/block/cube" "github.com/df-mc/dragonfly/server/block/cube/trace" + "github.com/df-mc/dragonfly/server/item" "github.com/df-mc/dragonfly/server/player" "github.com/df-mc/dragonfly/server/player/chat" "github.com/df-mc/dragonfly/server/world" @@ -34,6 +35,7 @@ func main() { srv.Listen() for srv.Accept(func(p *player.Player) { p.Handle(newRedstonePlayerHandler(p)) + p.Inventory().AddItem(item.NewStack(block.RedstoneTorch{}, 1)) }) { } } diff --git a/server/block/diode.go b/server/block/diode.go new file mode 100644 index 000000000..bd5a2ecd1 --- /dev/null +++ b/server/block/diode.go @@ -0,0 +1 @@ +package block diff --git a/server/block/hash.go b/server/block/hash.go index 6cfa5aa12..fd993d2f1 100644 --- a/server/block/hash.go +++ b/server/block/hash.go @@ -131,9 +131,9 @@ const ( hashRawGold hashRawIron hashRedstoneBlock - hashRedstoneDust hashRedstoneLamp hashRedstoneTorch + hashRedstoneWire hashReinforcedDeepslate hashSand hashSandstone @@ -686,10 +686,6 @@ func (RedstoneBlock) Hash() uint64 { return hashRedstoneBlock } -func (r RedstoneWire) Hash() uint64 { - return hashRedstoneDust | uint64(r.Power)<<8 -} - func (l RedstoneLamp) Hash() uint64 { return hashRedstoneLamp | uint64(boolByte(l.Lit))<<8 } @@ -698,6 +694,10 @@ func (t RedstoneTorch) Hash() uint64 { return hashRedstoneTorch | uint64(t.Facing)<<8 | uint64(boolByte(t.Lit))<<11 } +func (r RedstoneWire) Hash() uint64 { + return hashRedstoneWire | uint64(r.Power)<<8 +} + func (ReinforcedDeepslate) Hash() uint64 { return hashReinforcedDeepslate } diff --git a/server/block/redstone.go b/server/block/redstone.go index f2ebde928..6f4ba09aa 100644 --- a/server/block/redstone.go +++ b/server/block/redstone.go @@ -6,6 +6,23 @@ import ( "github.com/df-mc/dragonfly/server/world" ) +// RedstoneUpdater represents a block that can be updated through a change in redstone signal. +type RedstoneUpdater interface { + // RedstoneUpdate is called when a change in redstone signal is computed. + RedstoneUpdate(pos cube.Pos, w *world.World) +} + +// wireNetwork implements a minimally-invasive bolt-on accelerator that performs a breadth-first search through redstone +// wires in order to more efficiently and compute new redstone wire power levels and determine the order in which other +// blocks should be updated. This implementation is heavily based off of RedstoneWireTurbo and MCHPRS. +type wireNetwork struct { + nodes []*wireNode + nodeCache map[cube.Pos]*wireNode + updateQueue [3][]*wireNode + currentWalkLayer uint32 +} + +// wireNode is a data structure to keep track of redstone wires and neighbours that will receive updates. type wireNode struct { visited bool @@ -21,13 +38,6 @@ type wireNode struct { layer uint32 } -type wireNetwork struct { - nodes []*wireNode - nodeCache map[cube.Pos]*wireNode - updateQueue [3][]*wireNode - currentWalkLayer uint32 -} - const ( wireHeadingNorth = 0 wireHeadingEast = 1 @@ -35,6 +45,8 @@ const ( wireHeadingWest = 3 ) +// updateSurroundingRedstone sets off the breadth-first walk through all redstone wires connected to the initial +// position triggered. This is the main entry point for the redstone update algorithm. func updateSurroundingRedstone(pos cube.Pos, w *world.World) { n := &wireNetwork{ nodeCache: make(map[cube.Pos]*wireNode), @@ -53,6 +65,9 @@ func updateSurroundingRedstone(pos cube.Pos, w *world.World) { n.breadthFirstWalk(w) } +// identifyNeighbours identifies the neighbouring positions of a given node, determines their types, and links them into +// the graph. After that, based on what nodes in the graph have been visited, the neighbours are reordered left-to-right +// relative to the direction of information flow. func (n *wireNetwork) identifyNeighbours(w *world.World, node *wireNode) { neighbours := computeRedstoneNeighbours(node.pos) neighboursVisited := make([]bool, 0, 24) @@ -117,6 +132,8 @@ func (n *wireNetwork) identifyNeighbours(w *world.World, node *wireNode) { n.orientNeighbours(&neighbourNodes, node, heading) } +// reordering contains lookup tables that completely remap neighbour positions into a left-to-right ordering, based on +// the cardinal direction that is determined to be forward. var reordering = [][]uint32{ {2, 3, 16, 19, 0, 4, 1, 5, 7, 8, 17, 20, 12, 13, 18, 21, 6, 9, 22, 14, 11, 10, 23, 15}, {2, 3, 16, 19, 4, 1, 5, 0, 17, 20, 12, 13, 18, 21, 7, 8, 22, 14, 11, 15, 23, 9, 6, 10}, @@ -124,6 +141,7 @@ var reordering = [][]uint32{ {2, 3, 16, 19, 5, 0, 4, 1, 18, 21, 7, 8, 17, 20, 12, 13, 23, 10, 6, 9, 22, 15, 11, 14}, } +// orientNeighbours reorders the neighbours of a node based on the direction that is determined to be forward. func (n *wireNetwork) orientNeighbours(src *[]*wireNode, dst *wireNode, heading uint32) { dst.oriented = true dst.neighbours = make([]*wireNode, 0, 24) @@ -132,6 +150,8 @@ func (n *wireNetwork) orientNeighbours(src *[]*wireNode, dst *wireNode, heading } } +// propagateChanges propagates changes for any redstone wire in layer N, informing the neighbours to recompute their +// states in layers N + 1 and N + 2. func (n *wireNetwork) propagateChanges(w *world.World, node *wireNode, layer uint32) { if !node.oriented { n.identifyNeighbours(w, node) @@ -154,6 +174,8 @@ func (n *wireNetwork) propagateChanges(w *world.World, node *wireNode, layer uin } } +// breadthFirstWalk performs a breadth-first (layer by layer) traversal through redstone wires, propagating value +// changes to neighbours in the order that they are visited. func (n *wireNetwork) breadthFirstWalk(w *world.World) { n.shiftQueue() n.currentWalkLayer = 1 @@ -164,7 +186,9 @@ func (n *wireNetwork) breadthFirstWalk(w *world.World) { n.updateNode(w, node, n.currentWalkLayer) continue } - // TODO: Send a regular block update. + if t, ok := node.block.(RedstoneUpdater); ok { + t.RedstoneUpdate(node.pos, w) + } } n.shiftQueue() @@ -174,12 +198,15 @@ func (n *wireNetwork) breadthFirstWalk(w *world.World) { n.currentWalkLayer = 0 } +// shiftQueue shifts the update queue, moving all nodes from the current layer to the next layer. The last queue is then +// simply invalidated. func (n *wireNetwork) shiftQueue() { n.updateQueue[0] = n.updateQueue[1] n.updateQueue[1] = n.updateQueue[2] n.updateQueue[2] = nil } +// updateNode processes a node which has had neighbouring redstone wires that have experienced value changes. func (n *wireNetwork) updateNode(w *world.World, node *wireNode, layer uint32) { node.visited = true @@ -198,6 +225,8 @@ var ( rsNeighboursDn = [...]uint32{8, 10, 12, 14} ) +// calculateCurrentChanges computes redstone wire power levels from neighboring blocks. Modifications cut the number of +// power level changes by about 45% from vanilla, and also synergies well with the breadth-first search implementation. func (n *wireNetwork) calculateCurrentChanges(w *world.World, node *wireNode) RedstoneWire { wire := node.block.(RedstoneWire) i := wire.Power @@ -242,6 +271,7 @@ func (n *wireNetwork) calculateCurrentChanges(w *world.World, node *wireNode) Re return wire } +// maxCurrentStrength computes a redstone wire's power level based on a cached state. func (n *wireNetwork) maxCurrentStrength(neighbour world.Block, strength int) int { if wire, ok := neighbour.(RedstoneWire); ok { return max(wire.Power, strength) @@ -249,6 +279,8 @@ func (n *wireNetwork) maxCurrentStrength(neighbour world.Block, strength int) in return strength } +// computeRedstoneNeighbours computes the neighbours of a redstone wire node, ignoring neighbours that don't necessarily +// need to be updated, but are in vanilla. func computeRedstoneNeighbours(pos cube.Pos) []cube.Pos { return []cube.Pos{ // Immediate neighbours, in the order of west, east, down, up, north, and finally south. @@ -285,6 +317,8 @@ func computeRedstoneNeighbours(pos cube.Pos) []cube.Pos { } } +// computeRedstoneHeading computes the cardinal direction that is "forward" given which redstone wires have been visited +// and which have not around the position currently being processed. func computeRedstoneHeading(rX, rZ int32) uint32 { code := (rX + 1) + 3*(rZ+1) switch code { diff --git a/server/block/redstone_block.go b/server/block/redstone_block.go index 5da9d2843..537d257f1 100644 --- a/server/block/redstone_block.go +++ b/server/block/redstone_block.go @@ -17,6 +17,11 @@ func (b RedstoneBlock) EncodeBlock() (string, map[string]any) { return "minecraft:redstone_block", nil } +// Source ... +func (b RedstoneBlock) Source() bool { + return true +} + // WeakPower ... func (b RedstoneBlock) WeakPower(cube.Pos, cube.Face, *world.World, bool) int { return 15 diff --git a/server/block/redstone_lamp.go b/server/block/redstone_lamp.go index e8415bc05..37ec43910 100644 --- a/server/block/redstone_lamp.go +++ b/server/block/redstone_lamp.go @@ -50,8 +50,8 @@ func (l RedstoneLamp) UseOnBlock(pos cube.Pos, face cube.Face, _ mgl64.Vec3, w * return placed(ctx) } -// NeighbourUpdateTick ... -func (l RedstoneLamp) NeighbourUpdateTick(pos, _ cube.Pos, w *world.World) { +// RedstoneUpdate ... +func (l RedstoneLamp) RedstoneUpdate(pos cube.Pos, w *world.World) { if l.Lit != l.receivedRedstonePower(pos, w) { l.Lit = !l.Lit w.SetBlock(pos, l, nil) diff --git a/server/block/redstone_torch.go b/server/block/redstone_torch.go index 3d22b4996..86249badf 100644 --- a/server/block/redstone_torch.go +++ b/server/block/redstone_torch.go @@ -20,7 +20,9 @@ type RedstoneTorch struct { // BreakInfo ... func (t RedstoneTorch) BreakInfo() BreakInfo { - return newBreakInfo(0, alwaysHarvestable, nothingEffective, oneOf(t)) + return newBreakInfo(0, alwaysHarvestable, nothingEffective, oneOf(t)).withBreakHandler(func(pos cube.Pos, w *world.World, _ item.User) { + updateSurroundingRedstone(pos, w) + }) } // LightEmissionLevel ... @@ -60,7 +62,11 @@ func (t RedstoneTorch) UseOnBlock(pos cube.Pos, face cube.Face, _ mgl64.Vec3, w t.Lit = true place(w, pos, t, user, ctx) - return placed(ctx) + if placed(ctx) { + updateSurroundingRedstone(pos, w) + return true + } + return false } // NeighbourUpdateTick ... @@ -92,6 +98,11 @@ func (t RedstoneTorch) EncodeBlock() (name string, properties map[string]any) { return "minecraft:unlit_redstone_torch", map[string]any{"torch_facing_direction": face} } +// Source ... +func (t RedstoneTorch) Source() bool { + return true +} + // WeakPower ... func (t RedstoneTorch) WeakPower(_ cube.Pos, face cube.Face, _ *world.World, _ bool) int { if t.Lit && face != cube.FaceUp { diff --git a/server/block/redstone_wire.go b/server/block/redstone_wire.go index f5deadbc1..047a2a573 100644 --- a/server/block/redstone_wire.go +++ b/server/block/redstone_wire.go @@ -8,9 +8,15 @@ import ( "github.com/go-gl/mathgl/mgl64" ) +// RedstoneWire is a block that is used to transfer a charge between objects. Charged objects can be used to open doors +// or activate certain items. This block is the placed form of redstone which can be found by mining Redstone Ore with +// an Iron Pickaxe or better. Deactivated redstone wire will appear dark red, but activated redstone wire will appear +// bright red with a sparkling particle effect. type RedstoneWire struct { empty transparent + + // Power is the current power level of the redstone wire. It ranges from 0 to 15. Power int } @@ -62,6 +68,11 @@ func (r RedstoneWire) NeighbourUpdateTick(pos, _ cube.Pos, w *world.World) { } } +// Source ... +func (r RedstoneWire) Source() bool { + return true +} + // WeakPower ... func (r RedstoneWire) WeakPower(pos cube.Pos, face cube.Face, w *world.World, includeDust bool) int { if !includeDust { @@ -150,8 +161,8 @@ func (r RedstoneWire) canRunOnTop(w *world.World, pos cube.Pos, block world.Bloc return block.Model().FaceSolid(pos, cube.FaceUp, w) } -// allRedstoneDust returns a list of all redstone dust states. -func allRedstoneDust() (all []world.Block) { +// allRedstoneWires returns a list of all redstone dust states. +func allRedstoneWires() (all []world.Block) { for i := 0; i < 16; i++ { all = append(all, RedstoneWire{Power: i}) } diff --git a/server/block/register.go b/server/block/register.go index 066a8b540..aa8c880fb 100644 --- a/server/block/register.go +++ b/server/block/register.go @@ -171,7 +171,7 @@ func init() { registerAll(allPumpkins()) registerAll(allPurpurs()) registerAll(allQuartz()) - registerAll(allRedstoneDust()) + registerAll(allRedstoneWires()) registerAll(allRedstoneTorches()) registerAll(allSandstones()) registerAll(allSeaPickles()) diff --git a/server/world/block.go b/server/world/block.go index c36565b61..e3cca3883 100644 --- a/server/world/block.go +++ b/server/world/block.go @@ -52,6 +52,8 @@ type Liquid interface { // Conductor represents a block that can conduct a redstone signal. type Conductor interface { Block + // Source returns true if the conductor is a signal source. + Source() bool // WeakPower returns the power from a partial source and has limited usage. WeakPower(pos cube.Pos, face cube.Face, w *World, includeDust bool) int // StrongPower returns the power from a full source and can be passed to any redstone component. diff --git a/server/world/item.go b/server/world/item.go index d7e713c71..599e37592 100644 --- a/server/world/item.go +++ b/server/world/item.go @@ -81,6 +81,7 @@ func init() { for name, rid := range m { itemNamesToRuntimeIDs[name] = rid itemRuntimeIDsToNames[rid] = name + fmt.Println(name, rid) } } From 849ecb0625199d099ddd09a707f42e672bf486c2 Mon Sep 17 00:00:00 2001 From: JustTal Date: Tue, 17 Jan 2023 21:56:56 -0600 Subject: [PATCH 15/83] block/redstone_torch.go: broken redstone torch implementation lol --- go.mod | 2 ++ go.sum | 2 ++ main.go | 2 +- server/block/block.go | 7 +++++ server/block/redstone_lamp.go | 29 ++++++++++++++++-- server/block/redstone_torch.go | 54 ++++++++++++++++++++++++++++------ server/block/redstone_wire.go | 10 +++++-- server/block/register.go | 1 + server/block/torch.go | 17 ++++++----- server/session/player.go | 3 -- server/world/block_state.go | 1 + server/world/item.go | 1 - 12 files changed, 102 insertions(+), 27 deletions(-) diff --git a/go.mod b/go.mod index 748e64a10..06ef883fd 100644 --- a/go.mod +++ b/go.mod @@ -9,6 +9,7 @@ require ( github.com/df-mc/goleveldb v1.1.9 github.com/go-gl/mathgl v1.0.0 github.com/google/uuid v1.3.0 + github.com/kr/pretty v0.1.0 github.com/pelletier/go-toml v1.9.4 github.com/rogpeppe/go-internal v1.3.0 github.com/sandertv/gophertunnel v1.26.0 @@ -22,6 +23,7 @@ require ( github.com/golang/protobuf v1.5.2 // indirect github.com/golang/snappy v0.0.4 // indirect github.com/klauspost/compress v1.15.1 // indirect + github.com/kr/text v0.1.0 // indirect github.com/muhammadmuzzammil1998/jsonc v1.0.0 // indirect github.com/sandertv/go-raknet v1.12.0 // indirect github.com/stretchr/testify v1.7.0 // indirect diff --git a/go.sum b/go.sum index 9c11bb049..704b8595e 100644 --- a/go.sum +++ b/go.sum @@ -128,8 +128,10 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= github.com/klauspost/compress v1.15.1 h1:y9FcTHGyrebwfP0ZZqFiaxTaiDnUrGkJkI+f583BL1A= github.com/klauspost/compress v1.15.1/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/muhammadmuzzammil1998/jsonc v1.0.0 h1:8o5gBQn4ZA3NBA9DlTujCj2a4w0tqWrPVjDwhzkgTIs= github.com/muhammadmuzzammil1998/jsonc v1.0.0/go.mod h1:saF2fIVw4banK0H4+/EuqfFLpRnoy5S+ECwTOCcRcSU= diff --git a/main.go b/main.go index 49dadcd9b..7b0581d9b 100644 --- a/main.go +++ b/main.go @@ -35,7 +35,7 @@ func main() { srv.Listen() for srv.Accept(func(p *player.Player) { p.Handle(newRedstonePlayerHandler(p)) - p.Inventory().AddItem(item.NewStack(block.RedstoneTorch{}, 1)) + p.Inventory().AddItem(item.NewStack(block.Torch{}, 1)) }) { } } diff --git a/server/block/block.go b/server/block/block.go index f1812b4d5..1c275ede0 100644 --- a/server/block/block.go +++ b/server/block/block.go @@ -80,6 +80,13 @@ type Frictional interface { Friction() float64 } +var ( + // unknownDirection is a direction that is used for certain block items. This should not be exposed in the API. + unknownDirection = cube.Direction(len(cube.Directions())) + // unknownFace is a face that is used for certain block items. This should not be exposed in the API. + unknownFace = cube.Face(len(cube.Faces())) +) + func calculateFace(user item.User, placePos cube.Pos) cube.Face { userPos := user.Position() pos := cube.PosFromVec3(userPos) diff --git a/server/block/redstone_lamp.go b/server/block/redstone_lamp.go index 37ec43910..327d6b18d 100644 --- a/server/block/redstone_lamp.go +++ b/server/block/redstone_lamp.go @@ -5,10 +5,14 @@ import ( "github.com/df-mc/dragonfly/server/item" "github.com/df-mc/dragonfly/server/world" "github.com/go-gl/mathgl/mgl64" + "math/rand" + "time" ) +// RedstoneLamp is a block that produces light when activated with a redstone signal. type RedstoneLamp struct { solid + // Lit is if the redstone lamp is lit and emitting light. Lit bool } @@ -50,12 +54,31 @@ func (l RedstoneLamp) UseOnBlock(pos cube.Pos, face cube.Face, _ mgl64.Vec3, w * return placed(ctx) } +// NeighbourUpdateTick ... +func (l RedstoneLamp) NeighbourUpdateTick(pos, _ cube.Pos, w *world.World) { + l.RedstoneUpdate(pos, w) +} + // RedstoneUpdate ... func (l RedstoneLamp) RedstoneUpdate(pos cube.Pos, w *world.World) { - if l.Lit != l.receivedRedstonePower(pos, w) { - l.Lit = !l.Lit - w.SetBlock(pos, l, nil) + if l.Lit == l.receivedRedstonePower(pos, w) { + return + } + if !l.Lit { + l.Lit = true + w.SetBlock(pos, l, &world.SetOpts{DisableBlockUpdates: true}) + } else { + w.ScheduleBlockUpdate(pos, time.Millisecond*200) + } +} + +// ScheduledTick ... +func (l RedstoneLamp) ScheduledTick(pos cube.Pos, w *world.World, _ *rand.Rand) { + if l.receivedRedstonePower(pos, w) { + return } + l.Lit = false + w.SetBlock(pos, l, &world.SetOpts{DisableBlockUpdates: true}) } // receivedRedstonePower ... diff --git a/server/block/redstone_torch.go b/server/block/redstone_torch.go index 86249badf..378e6030c 100644 --- a/server/block/redstone_torch.go +++ b/server/block/redstone_torch.go @@ -5,6 +5,8 @@ import ( "github.com/df-mc/dragonfly/server/item" "github.com/df-mc/dragonfly/server/world" "github.com/go-gl/mathgl/mgl64" + "math/rand" + "time" ) // RedstoneTorch is a non-solid blocks that emits little light and also a full-strength redstone signal when lit. @@ -73,7 +75,26 @@ func (t RedstoneTorch) UseOnBlock(pos cube.Pos, face cube.Face, _ mgl64.Vec3, w func (t RedstoneTorch) NeighbourUpdateTick(pos, _ cube.Pos, w *world.World) { if !w.Block(pos.Side(t.Facing)).Model().FaceSolid(pos.Side(t.Facing), t.Facing.Opposite(), w) { w.SetBlock(pos, nil, nil) + dropItem(w, item.NewStack(t, 1), pos.Vec3Centre()) + return } + t.RedstoneUpdate(pos, w) +} + +// RedstoneUpdate ... +func (t RedstoneTorch) RedstoneUpdate(pos cube.Pos, w *world.World) { + face := t.Facing.Opposite() + if powered := w.EmittedRedstonePower(pos.Side(face), face, true) > 0; powered != t.Lit { + return + } + w.ScheduleBlockUpdate(pos, time.Millisecond*100) +} + +// ScheduledTick ... +func (t RedstoneTorch) ScheduledTick(pos cube.Pos, w *world.World, _ *rand.Rand) { + t.Lit = !t.Lit + w.SetBlock(pos, t, &world.SetOpts{DisableBlockUpdates: true}) + updateSurroundingRedstone(pos, w) } // HasLiquidDrops ... @@ -88,9 +109,12 @@ func (t RedstoneTorch) EncodeItem() (name string, meta int16) { // EncodeBlock ... func (t RedstoneTorch) EncodeBlock() (name string, properties map[string]any) { - face := t.Facing.String() - if t.Facing == cube.FaceDown { - face = "top" + face := "unknown" + if t.Facing != unknownFace { + face = t.Facing.String() + if t.Facing == cube.FaceDown { + face = "top" + } } if t.Lit { return "minecraft:redstone_torch", map[string]any{"torch_facing_direction": face} @@ -100,12 +124,21 @@ func (t RedstoneTorch) EncodeBlock() (name string, properties map[string]any) { // Source ... func (t RedstoneTorch) Source() bool { - return true + return t.Lit } // WeakPower ... func (t RedstoneTorch) WeakPower(_ cube.Pos, face cube.Face, _ *world.World, _ bool) int { - if t.Lit && face != cube.FaceUp { + if !t.Lit { + return 0 + } + if face == cube.FaceDown { + if t.Facing != cube.FaceDown { + return 15 + } + return 0 + } + if face != t.Facing { return 15 } return 0 @@ -113,17 +146,20 @@ func (t RedstoneTorch) WeakPower(_ cube.Pos, face cube.Face, _ *world.World, _ b // StrongPower ... func (t RedstoneTorch) StrongPower(cube.Pos, cube.Face, *world.World, bool) int { + if t.Lit && t.Facing == cube.FaceDown { + return 15 + } return 0 } // allRedstoneTorches ... func allRedstoneTorches() (all []world.Block) { - for i := cube.Face(0); i < 6; i++ { - if i == cube.FaceUp { + for _, f := range append(cube.Faces(), unknownFace) { + if f == cube.FaceUp { continue } - all = append(all, RedstoneTorch{Facing: i, Lit: true}) - all = append(all, RedstoneTorch{Facing: i}) + all = append(all, RedstoneTorch{Facing: f, Lit: true}) + all = append(all, RedstoneTorch{Facing: f}) } return } diff --git a/server/block/redstone_wire.go b/server/block/redstone_wire.go index 047a2a573..78a8d0083 100644 --- a/server/block/redstone_wire.go +++ b/server/block/redstone_wire.go @@ -60,8 +60,12 @@ func (r RedstoneWire) UseOnBlock(pos cube.Pos, face cube.Face, _ mgl64.Vec3, w * // NeighbourUpdateTick ... func (r RedstoneWire) NeighbourUpdateTick(pos, _ cube.Pos, w *world.World) { - power := r.calculatePower(pos, w) - if r.Power != power { + if _, ok := w.Block(pos.Side(cube.FaceDown)).(Air); ok { + w.SetBlock(pos, nil, nil) + dropItem(w, item.NewStack(r, 1), pos.Vec3Centre()) + return + } + if power := r.calculatePower(pos, w); r.Power != power { r.Power = power w.SetBlock(pos, r, &world.SetOpts{DisableBlockUpdates: true}) updateSurroundingRedstone(pos, w) @@ -70,7 +74,7 @@ func (r RedstoneWire) NeighbourUpdateTick(pos, _ cube.Pos, w *world.World) { // Source ... func (r RedstoneWire) Source() bool { - return true + return false } // WeakPower ... diff --git a/server/block/register.go b/server/block/register.go index aa8c880fb..1d1495075 100644 --- a/server/block/register.go +++ b/server/block/register.go @@ -310,6 +310,7 @@ func init() { world.RegisterItem(RedstoneBlock{}) world.RegisterItem(RedstoneWire{}) world.RegisterItem(RedstoneTorch{}) + world.RegisterItem(RedstoneLamp{}) world.RegisterItem(ReinforcedDeepslate{}) world.RegisterItem(Sand{Red: true}) world.RegisterItem(Sand{}) diff --git a/server/block/torch.go b/server/block/torch.go index 98056d195..93591b838 100644 --- a/server/block/torch.go +++ b/server/block/torch.go @@ -90,9 +90,12 @@ func (t Torch) EncodeItem() (name string, meta int16) { // EncodeBlock ... func (t Torch) EncodeBlock() (name string, properties map[string]any) { - face := t.Facing.String() - if t.Facing == cube.FaceDown { - face = "top" + face := "unknown" + if t.Facing != unknownFace { + face = t.Facing.String() + if t.Facing == cube.FaceDown { + face = "top" + } } switch t.Type { case NormalFire(): @@ -105,12 +108,12 @@ func (t Torch) EncodeBlock() (name string, properties map[string]any) { // allTorches ... func allTorches() (torch []world.Block) { - for i := cube.Face(0); i < 6; i++ { - if i == cube.FaceUp { + for _, f := range append(cube.Faces(), unknownFace) { + if f == cube.FaceUp { continue } - torch = append(torch, Torch{Type: NormalFire(), Facing: i}) - torch = append(torch, Torch{Type: SoulFire(), Facing: i}) + torch = append(torch, Torch{Type: NormalFire(), Facing: f}) + torch = append(torch, Torch{Type: SoulFire(), Facing: f}) } return } diff --git a/server/session/player.go b/server/session/player.go index c5d291453..d8ce8394b 100644 --- a/server/session/player.go +++ b/server/session/player.go @@ -724,9 +724,6 @@ func stackToItem(it protocol.ItemStack) item.Stack { t = block.Air{} } if it.BlockRuntimeID > 0 { - // It shouldn't matter if it (for whatever reason) wasn't able to get the block runtime ID, - // since on the next line, we assert that the block is an item. If it didn't succeed, it'll - // return air anyway. b, _ := world.BlockByRuntimeID(uint32(it.BlockRuntimeID)) if t, ok = b.(world.Item); !ok { t = block.Air{} diff --git a/server/world/block_state.go b/server/world/block_state.go index 08dde1bab..49cd8af6b 100644 --- a/server/world/block_state.go +++ b/server/world/block_state.go @@ -69,6 +69,7 @@ func registerBlockState(s blockState) { if _, ok := stateRuntimeIDs[h]; ok { panic(fmt.Sprintf("cannot register the same state twice (%+v)", s)) } + rid := uint32(len(blocks)) if s.Name == "minecraft:air" { airRID = rid diff --git a/server/world/item.go b/server/world/item.go index 599e37592..d7e713c71 100644 --- a/server/world/item.go +++ b/server/world/item.go @@ -81,7 +81,6 @@ func init() { for name, rid := range m { itemNamesToRuntimeIDs[name] = rid itemRuntimeIDsToNames[rid] = name - fmt.Println(name, rid) } } From 2d8f704fb771408029c3698116e560ea3d33bbe6 Mon Sep 17 00:00:00 2001 From: JustTal Date: Tue, 17 Jan 2023 22:04:23 -0600 Subject: [PATCH 16/83] block/redstone_torch.go: dont invert facing --- server/block/redstone_torch.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/server/block/redstone_torch.go b/server/block/redstone_torch.go index 378e6030c..61227348b 100644 --- a/server/block/redstone_torch.go +++ b/server/block/redstone_torch.go @@ -83,8 +83,7 @@ func (t RedstoneTorch) NeighbourUpdateTick(pos, _ cube.Pos, w *world.World) { // RedstoneUpdate ... func (t RedstoneTorch) RedstoneUpdate(pos cube.Pos, w *world.World) { - face := t.Facing.Opposite() - if powered := w.EmittedRedstonePower(pos.Side(face), face, true) > 0; powered != t.Lit { + if powered := w.EmittedRedstonePower(pos.Side(t.Facing), t.Facing, true) > 0; powered != t.Lit { return } w.ScheduleBlockUpdate(pos, time.Millisecond*100) From eb00f59e4446e2c6297dcff8dff3bacc60b30365 Mon Sep 17 00:00:00 2001 From: JustTal Date: Tue, 17 Jan 2023 22:31:44 -0600 Subject: [PATCH 17/83] block/redstone_torch.go: power output kinda fixed? --- server/block/redstone_torch.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/server/block/redstone_torch.go b/server/block/redstone_torch.go index 61227348b..eb43cb5ae 100644 --- a/server/block/redstone_torch.go +++ b/server/block/redstone_torch.go @@ -132,20 +132,20 @@ func (t RedstoneTorch) WeakPower(_ cube.Pos, face cube.Face, _ *world.World, _ b return 0 } if face == cube.FaceDown { - if t.Facing != cube.FaceDown { + if t.Facing.Opposite() != cube.FaceDown { return 15 } return 0 } - if face != t.Facing { + if face != t.Facing.Opposite() { return 15 } return 0 } // StrongPower ... -func (t RedstoneTorch) StrongPower(cube.Pos, cube.Face, *world.World, bool) int { - if t.Lit && t.Facing == cube.FaceDown { +func (t RedstoneTorch) StrongPower(_ cube.Pos, face cube.Face, _ *world.World, _ bool) int { + if t.Lit && face == cube.FaceUp { return 15 } return 0 From 3bc65243dfbb60e78a514587395362fe15f131ae Mon Sep 17 00:00:00 2001 From: JustTal Date: Tue, 17 Jan 2023 22:59:44 -0600 Subject: [PATCH 18/83] block/redstone_torch.go: i think it works (mostly) --- server/block/redstone_torch.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/block/redstone_torch.go b/server/block/redstone_torch.go index eb43cb5ae..60b23977a 100644 --- a/server/block/redstone_torch.go +++ b/server/block/redstone_torch.go @@ -83,7 +83,7 @@ func (t RedstoneTorch) NeighbourUpdateTick(pos, _ cube.Pos, w *world.World) { // RedstoneUpdate ... func (t RedstoneTorch) RedstoneUpdate(pos cube.Pos, w *world.World) { - if powered := w.EmittedRedstonePower(pos.Side(t.Facing), t.Facing, true) > 0; powered != t.Lit { + if w.EmittedRedstonePower(pos.Side(t.Facing), t.Facing, true) > 0 != t.Lit { return } w.ScheduleBlockUpdate(pos, time.Millisecond*100) @@ -145,7 +145,7 @@ func (t RedstoneTorch) WeakPower(_ cube.Pos, face cube.Face, _ *world.World, _ b // StrongPower ... func (t RedstoneTorch) StrongPower(_ cube.Pos, face cube.Face, _ *world.World, _ bool) int { - if t.Lit && face == cube.FaceUp { + if t.Lit && face == cube.FaceDown { return 15 } return 0 From 93a369bff9b52035f994b0ecb286346e78059e4a Mon Sep 17 00:00:00 2001 From: JustTal Date: Tue, 17 Jan 2023 23:16:13 -0600 Subject: [PATCH 19/83] block/redstone_torch.go: Add a TODO for burnout. --- server/block/redstone_torch.go | 1 + 1 file changed, 1 insertion(+) diff --git a/server/block/redstone_torch.go b/server/block/redstone_torch.go index 60b23977a..a34656b4e 100644 --- a/server/block/redstone_torch.go +++ b/server/block/redstone_torch.go @@ -10,6 +10,7 @@ import ( ) // RedstoneTorch is a non-solid blocks that emits little light and also a full-strength redstone signal when lit. +// TODO: Redstone torches should burn out when used too recently and excessively. type RedstoneTorch struct { transparent empty From 6a670c0c209acba4b23a6e7c05554f86d3373253 Mon Sep 17 00:00:00 2001 From: JustTal Date: Wed, 18 Jan 2023 18:20:04 -0600 Subject: [PATCH 20/83] block/redstone_repeater.go: repeaters... kinda --- server/block/hash.go | 5 + server/block/model/diode.go | 19 ++++ server/block/redstone_repeater.go | 167 ++++++++++++++++++++++++++++++ server/block/redstone_torch.go | 22 ++-- server/block/redstone_wire.go | 31 +++--- server/block/register.go | 8 +- server/world/block_state.go | 5 + 7 files changed, 230 insertions(+), 27 deletions(-) create mode 100644 server/block/model/diode.go create mode 100644 server/block/redstone_repeater.go diff --git a/server/block/hash.go b/server/block/hash.go index fd993d2f1..ebf105336 100644 --- a/server/block/hash.go +++ b/server/block/hash.go @@ -132,6 +132,7 @@ const ( hashRawIron hashRedstoneBlock hashRedstoneLamp + hashRedstoneRepeater hashRedstoneTorch hashRedstoneWire hashReinforcedDeepslate @@ -690,6 +691,10 @@ func (l RedstoneLamp) Hash() uint64 { return hashRedstoneLamp | uint64(boolByte(l.Lit))<<8 } +func (r RedstoneRepeater) Hash() uint64 { + return hashRedstoneRepeater | uint64(r.Facing)<<8 | uint64(boolByte(r.Powered))<<10 | uint64(r.Delay)<<11 +} + func (t RedstoneTorch) Hash() uint64 { return hashRedstoneTorch | uint64(t.Facing)<<8 | uint64(boolByte(t.Lit))<<11 } diff --git a/server/block/model/diode.go b/server/block/model/diode.go new file mode 100644 index 000000000..16c47a2bd --- /dev/null +++ b/server/block/model/diode.go @@ -0,0 +1,19 @@ +package model + +import ( + "github.com/df-mc/dragonfly/server/block/cube" + "github.com/df-mc/dragonfly/server/world" +) + +// Diode is a model used by redstone diodes, such as repeaters and comparators. +type Diode struct{} + +// BBox ... +func (Diode) BBox(cube.Pos, *world.World) []cube.BBox { + return []cube.BBox{full.ExtendTowards(cube.FaceDown, 0.875)} +} + +// FaceSolid ... +func (Diode) FaceSolid(cube.Pos, cube.Face, *world.World) bool { + return false +} diff --git a/server/block/redstone_repeater.go b/server/block/redstone_repeater.go new file mode 100644 index 000000000..e48991706 --- /dev/null +++ b/server/block/redstone_repeater.go @@ -0,0 +1,167 @@ +package block + +import ( + "github.com/df-mc/dragonfly/server/block/cube" + "github.com/df-mc/dragonfly/server/block/model" + "github.com/df-mc/dragonfly/server/item" + "github.com/df-mc/dragonfly/server/world" + "github.com/go-gl/mathgl/mgl64" + "math/rand" + "time" +) + +// RedstoneRepeater is a block used in redstone circuits to "repeat" redstone signals back to full strength, delay +// signals, prevent signals moving backwards, or to "lock" signals in one state. +type RedstoneRepeater struct { + transparent + + // Facing is the direction from the torch to the block. + Facing cube.Direction + // Powered is true if the repeater is powered by a redstone signal. + Powered bool + // Delay represents the delay of the repeater in redstone ticks. It is between the range of one to four. + Delay int +} + +// HasLiquidDrops ... +func (RedstoneRepeater) HasLiquidDrops() bool { + return true +} + +// Model ... +func (RedstoneRepeater) Model() world.BlockModel { + return model.Diode{} +} + +// BreakInfo ... +func (r RedstoneRepeater) BreakInfo() BreakInfo { + return newBreakInfo(0, alwaysHarvestable, nothingEffective, oneOf(r)).withBreakHandler(func(pos cube.Pos, w *world.World, _ item.User) { + updateSurroundingRedstone(pos, w) + }) +} + +// EncodeItem ... +func (RedstoneRepeater) EncodeItem() (name string, meta int16) { + return "minecraft:repeater", 0 +} + +// EncodeBlock ... +func (r RedstoneRepeater) EncodeBlock() (string, map[string]any) { + if r.Powered { + return "minecraft:powered_repeater", map[string]any{"direction": int32(horizontalDirection(r.Facing)), "repeater_delay": int32(r.Delay)} + } + return "minecraft:unpowered_repeater", map[string]any{"direction": int32(horizontalDirection(r.Facing)), "repeater_delay": int32(r.Delay)} +} + +// UseOnBlock ... +func (r RedstoneRepeater) UseOnBlock(pos cube.Pos, face cube.Face, _ mgl64.Vec3, w *world.World, user item.User, ctx *item.UseContext) bool { + pos, _, used := firstReplaceable(w, pos, face, r) + if !used { + return false + } + if d, ok := w.Block(pos.Side(cube.FaceDown)).(LightDiffuser); ok && d.LightDiffusionLevel() == 0 { + return false + } + r.Facing = user.Rotation().Direction().Opposite() + + place(w, pos, r, user, ctx) + if placed(ctx) { + r.RedstoneUpdate(pos, w) + return true + } + return false +} + +// NeighbourUpdateTick ... +func (r RedstoneRepeater) NeighbourUpdateTick(pos, _ cube.Pos, w *world.World) { + if d, ok := w.Block(pos.Side(cube.FaceDown)).(LightDiffuser); ok && d.LightDiffusionLevel() == 0 { + w.SetBlock(pos, nil, nil) + dropItem(w, item.NewStack(r, 1), pos.Vec3Centre()) + return + } + r.RedstoneUpdate(pos, w) +} + +// Activate ... +func (r RedstoneRepeater) Activate(pos cube.Pos, _ cube.Face, w *world.World, u item.User, _ *item.UseContext) bool { + if r.Delay++; r.Delay > 3 { + r.Delay = 0 + } + w.SetBlock(pos, r, nil) + return false +} + +// RedstoneUpdate ... +func (r RedstoneRepeater) RedstoneUpdate(pos cube.Pos, w *world.World) { + if r.Locked() { + // Ignore this update; the repeater is locked. + return + } + if r.inputStrength(pos, w) > 0 != r.Powered { + w.ScheduleBlockUpdate(pos, time.Duration(r.Delay+1)*time.Millisecond*100) + } +} + +// ScheduledTick ... +func (r RedstoneRepeater) ScheduledTick(pos cube.Pos, w *world.World, _ *rand.Rand) { + if r.Locked() { + // Ignore this tick; the repeater is locked. + return + } + + if !r.Powered && r.inputStrength(pos, w) <= 0 { + w.ScheduleBlockUpdate(pos, time.Duration(r.Delay+1)*time.Millisecond*100) + } + + r.Powered = !r.Powered + w.SetBlock(pos, r, &world.SetOpts{DisableBlockUpdates: true}) + updateSurroundingRedstone(pos, w) +} + +// Locked ... +func (RedstoneRepeater) Locked() bool { + //TODO implement me + return false +} + +// Source ... +func (r RedstoneRepeater) Source() bool { + return r.Powered +} + +// WeakPower ... +func (r RedstoneRepeater) WeakPower(_ cube.Pos, face cube.Face, _ *world.World, _ bool) int { + if r.Powered && face == r.Facing.Face() { + return 15 + } + return 0 +} + +// StrongPower ... +func (r RedstoneRepeater) StrongPower(pos cube.Pos, face cube.Face, w *world.World, includeDust bool) int { + return r.WeakPower(pos, face, w, includeDust) +} + +// inputStrength ... +func (r RedstoneRepeater) inputStrength(pos cube.Pos, w *world.World) int { + sidePos := pos.Side(r.Facing.Face()) + if strength := w.EmittedRedstonePower(sidePos, r.Facing.Face(), true); strength > 0 { + return strength + } + if w, ok := w.Block(sidePos).(RedstoneWire); ok { + return w.Power + } + return 0 +} + +// allRedstoneRepeaters ... +func allRedstoneRepeaters() (repeaters []world.Block) { + for _, d := range cube.Directions() { + for _, p := range []bool{false, true} { + for i := 0; i < 4; i++ { + repeaters = append(repeaters, RedstoneRepeater{Facing: d, Delay: i, Powered: p}) + } + } + } + return +} diff --git a/server/block/redstone_torch.go b/server/block/redstone_torch.go index a34656b4e..04fa29779 100644 --- a/server/block/redstone_torch.go +++ b/server/block/redstone_torch.go @@ -21,11 +21,9 @@ type RedstoneTorch struct { Lit bool } -// BreakInfo ... -func (t RedstoneTorch) BreakInfo() BreakInfo { - return newBreakInfo(0, alwaysHarvestable, nothingEffective, oneOf(t)).withBreakHandler(func(pos cube.Pos, w *world.World, _ item.User) { - updateSurroundingRedstone(pos, w) - }) +// HasLiquidDrops ... +func (RedstoneTorch) HasLiquidDrops() bool { + return true } // LightEmissionLevel ... @@ -36,6 +34,13 @@ func (t RedstoneTorch) LightEmissionLevel() uint8 { return 0 } +// BreakInfo ... +func (t RedstoneTorch) BreakInfo() BreakInfo { + return newBreakInfo(0, alwaysHarvestable, nothingEffective, oneOf(t)).withBreakHandler(func(pos cube.Pos, w *world.World, _ item.User) { + updateSurroundingRedstone(pos, w) + }) +} + // UseOnBlock ... func (t RedstoneTorch) UseOnBlock(pos cube.Pos, face cube.Face, _ mgl64.Vec3, w *world.World, user item.User, ctx *item.UseContext) bool { pos, face, used := firstReplaceable(w, pos, face, t) @@ -97,13 +102,8 @@ func (t RedstoneTorch) ScheduledTick(pos cube.Pos, w *world.World, _ *rand.Rand) updateSurroundingRedstone(pos, w) } -// HasLiquidDrops ... -func (t RedstoneTorch) HasLiquidDrops() bool { - return true -} - // EncodeItem ... -func (t RedstoneTorch) EncodeItem() (name string, meta int16) { +func (RedstoneTorch) EncodeItem() (name string, meta int16) { return "minecraft:redstone_torch", 0 } diff --git a/server/block/redstone_wire.go b/server/block/redstone_wire.go index 78a8d0083..54c993979 100644 --- a/server/block/redstone_wire.go +++ b/server/block/redstone_wire.go @@ -20,6 +20,11 @@ type RedstoneWire struct { Power int } +// HasLiquidDrops ... +func (RedstoneWire) HasLiquidDrops() bool { + return true +} + // BreakInfo ... func (r RedstoneWire) BreakInfo() BreakInfo { return newBreakInfo(0, alwaysHarvestable, nothingEffective, oneOf(r)).withBreakHandler(func(pos cube.Pos, w *world.World, _ item.User) { @@ -28,7 +33,7 @@ func (r RedstoneWire) BreakInfo() BreakInfo { } // EncodeItem ... -func (r RedstoneWire) EncodeItem() (name string, meta int16) { +func (RedstoneWire) EncodeItem() (name string, meta int16) { return "minecraft:redstone", 0 } @@ -73,7 +78,7 @@ func (r RedstoneWire) NeighbourUpdateTick(pos, _ cube.Pos, w *world.World) { } // Source ... -func (r RedstoneWire) Source() bool { +func (RedstoneWire) Source() bool { return false } @@ -128,7 +133,7 @@ func (r RedstoneWire) calculatePower(pos cube.Pos, w *world.World) int { } // maxCurrentStrength ... -func (r RedstoneWire) maxCurrentStrength(power int, pos cube.Pos, w *world.World) int { +func (RedstoneWire) maxCurrentStrength(power int, pos cube.Pos, w *world.World) int { if wire, ok := w.Block(pos).(RedstoneWire); ok { return max(wire.Power, power) } @@ -139,28 +144,28 @@ func (r RedstoneWire) maxCurrentStrength(power int, pos cube.Pos, w *world.World func (r RedstoneWire) connection(pos cube.Pos, face cube.Face, w *world.World) bool { sidePos := pos.Side(face) sideBlock := w.Block(sidePos) - if _, solidAbove := w.Block(pos.Side(cube.FaceUp)).Model().(model.Solid); !solidAbove && r.canRunOnTop(w, sidePos, sideBlock) && r.connectsTo(w.Block(sidePos.Side(cube.FaceUp)), false) { + if _, solidAbove := w.Block(pos.Side(cube.FaceUp)).Model().(model.Solid); !solidAbove && r.canRunOnTop(w, sidePos, sideBlock) && r.connectsTo(w.Block(sidePos.Side(cube.FaceUp)), face, false) { return true } _, sideSolid := sideBlock.Model().(model.Solid) - return r.connectsTo(sideBlock, true) || !sideSolid && r.connectsTo(w.Block(sidePos.Side(cube.FaceDown)), false) + return r.connectsTo(sideBlock, face, true) || !sideSolid && r.connectsTo(w.Block(sidePos.Side(cube.FaceDown)), face, false) } // connectsTo ... -func (r RedstoneWire) connectsTo(block world.Block, hasFace bool) bool { - switch block.(type) { +func (RedstoneWire) connectsTo(block world.Block, face cube.Face, allowDirectSources bool) bool { + switch r := block.(type) { case RedstoneWire: return true - // TODO: Repeaters, observers + case RedstoneRepeater: + return r.Facing.Face() == face || r.Facing.Face().Opposite() == face } - if _, ok := block.(world.Conductor); ok { - return hasFace - } - return false + // TODO: Account for observers. + c, ok := block.(world.Conductor) + return ok && allowDirectSources && c.Source() } // canRunOnTop ... -func (r RedstoneWire) canRunOnTop(w *world.World, pos cube.Pos, block world.Block) bool { +func (RedstoneWire) canRunOnTop(w *world.World, pos cube.Pos, block world.Block) bool { // TODO: Hoppers. return block.Model().FaceSolid(pos, cube.FaceUp, w) } diff --git a/server/block/register.go b/server/block/register.go index 1d1495075..4838ba1a8 100644 --- a/server/block/register.go +++ b/server/block/register.go @@ -171,8 +171,9 @@ func init() { registerAll(allPumpkins()) registerAll(allPurpurs()) registerAll(allQuartz()) - registerAll(allRedstoneWires()) + registerAll(allRedstoneRepeaters()) registerAll(allRedstoneTorches()) + registerAll(allRedstoneWires()) registerAll(allSandstones()) registerAll(allSeaPickles()) registerAll(allSigns()) @@ -308,9 +309,10 @@ func init() { world.RegisterItem(RawGold{}) world.RegisterItem(RawIron{}) world.RegisterItem(RedstoneBlock{}) - world.RegisterItem(RedstoneWire{}) - world.RegisterItem(RedstoneTorch{}) world.RegisterItem(RedstoneLamp{}) + world.RegisterItem(RedstoneRepeater{}) + world.RegisterItem(RedstoneTorch{}) + world.RegisterItem(RedstoneWire{}) world.RegisterItem(ReinforcedDeepslate{}) world.RegisterItem(Sand{Red: true}) world.RegisterItem(Sand{}) diff --git a/server/world/block_state.go b/server/world/block_state.go index 49cd8af6b..82aed966c 100644 --- a/server/world/block_state.go +++ b/server/world/block_state.go @@ -5,6 +5,7 @@ import ( _ "embed" "fmt" "github.com/df-mc/dragonfly/server/world/chunk" + "github.com/kr/pretty" "github.com/sandertv/gophertunnel/minecraft/nbt" "math" "sort" @@ -65,6 +66,10 @@ func init() { // registerBlockState registers a new blockState to the states slice. The function panics if the properties the // blockState hold are invalid or if the blockState was already registered. func registerBlockState(s blockState) { + if strings.Contains(s.Name, "repeater") { + pretty.Println(s) + } + h := stateHash{name: s.Name, properties: hashProperties(s.Properties)} if _, ok := stateRuntimeIDs[h]; ok { panic(fmt.Sprintf("cannot register the same state twice (%+v)", s)) From 0d2643fe5c56e64c6fcdc96aa1079de583d540e4 Mon Sep 17 00:00:00 2001 From: JustTal Date: Wed, 18 Jan 2023 18:26:28 -0600 Subject: [PATCH 21/83] block/redstone_repeater.go: oops --- server/block/redstone_repeater.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/server/block/redstone_repeater.go b/server/block/redstone_repeater.go index e48991706..c10e13a86 100644 --- a/server/block/redstone_repeater.go +++ b/server/block/redstone_repeater.go @@ -109,13 +109,13 @@ func (r RedstoneRepeater) ScheduledTick(pos cube.Pos, w *world.World, _ *rand.Ra return } - if !r.Powered && r.inputStrength(pos, w) <= 0 { - w.ScheduleBlockUpdate(pos, time.Duration(r.Delay+1)*time.Millisecond*100) - } - r.Powered = !r.Powered w.SetBlock(pos, r, &world.SetOpts{DisableBlockUpdates: true}) updateSurroundingRedstone(pos, w) + + if r.Powered && r.inputStrength(pos, w) <= 0 { + w.ScheduleBlockUpdate(pos, time.Duration(r.Delay+1)*time.Millisecond*100) + } } // Locked ... From 3bac09dffc2fabaef103dfc8276be62c9daed0e5 Mon Sep 17 00:00:00 2001 From: JustTal Date: Wed, 18 Jan 2023 19:09:17 -0600 Subject: [PATCH 22/83] block/redstone_repeater.go: oops v3 --- server/block/model/diode.go | 2 +- server/block/redstone.go | 26 +++++++++++++++++++++++--- server/block/redstone_repeater.go | 8 ++++---- server/block/redstone_torch.go | 6 +++--- server/block/redstone_wire.go | 6 +++--- 5 files changed, 34 insertions(+), 14 deletions(-) diff --git a/server/block/model/diode.go b/server/block/model/diode.go index 16c47a2bd..8f9bc461f 100644 --- a/server/block/model/diode.go +++ b/server/block/model/diode.go @@ -5,7 +5,7 @@ import ( "github.com/df-mc/dragonfly/server/world" ) -// Diode is a model used by redstone diodes, such as repeaters and comparators. +// Diode is a model used by redstone gates, such as repeaters and comparators. type Diode struct{} // BBox ... diff --git a/server/block/redstone.go b/server/block/redstone.go index 6f4ba09aa..3b846861a 100644 --- a/server/block/redstone.go +++ b/server/block/redstone.go @@ -45,9 +45,9 @@ const ( wireHeadingWest = 3 ) -// updateSurroundingRedstone sets off the breadth-first walk through all redstone wires connected to the initial -// position triggered. This is the main entry point for the redstone update algorithm. -func updateSurroundingRedstone(pos cube.Pos, w *world.World) { +// updateRedstoneWires sets off the breadth-first walk through all redstone wires connected to the initial position +// triggered. This is the main entry point for the redstone update algorithm. +func updateRedstoneWires(pos cube.Pos, w *world.World) { n := &wireNetwork{ nodeCache: make(map[cube.Pos]*wireNode), updateQueue: [3][]*wireNode{}, @@ -65,6 +65,26 @@ func updateSurroundingRedstone(pos cube.Pos, w *world.World) { n.breadthFirstWalk(w) } +// updateGateRedstone is used to update redstone gates on each face of the given offset centre position. +func updateGateRedstone(centre cube.Pos, w *world.World, face cube.Face) { + pos := centre.Side(face) + if r, ok := w.Block(pos).(RedstoneUpdater); ok { + r.RedstoneUpdate(pos, w) + } + + opposite := face.Opposite() + for _, f := range cube.Faces() { + if f == opposite { + continue + } + + sidePos := pos.Side(f) + if r, ok := w.Block(sidePos).(RedstoneUpdater); ok { + r.RedstoneUpdate(sidePos, w) + } + } +} + // identifyNeighbours identifies the neighbouring positions of a given node, determines their types, and links them into // the graph. After that, based on what nodes in the graph have been visited, the neighbours are reordered left-to-right // relative to the direction of information flow. diff --git a/server/block/redstone_repeater.go b/server/block/redstone_repeater.go index c10e13a86..e1b2c7a86 100644 --- a/server/block/redstone_repeater.go +++ b/server/block/redstone_repeater.go @@ -36,7 +36,7 @@ func (RedstoneRepeater) Model() world.BlockModel { // BreakInfo ... func (r RedstoneRepeater) BreakInfo() BreakInfo { return newBreakInfo(0, alwaysHarvestable, nothingEffective, oneOf(r)).withBreakHandler(func(pos cube.Pos, w *world.World, _ item.User) { - updateSurroundingRedstone(pos, w) + updateGateRedstone(pos, w, r.Facing.Face().Opposite()) }) } @@ -83,7 +83,7 @@ func (r RedstoneRepeater) NeighbourUpdateTick(pos, _ cube.Pos, w *world.World) { } // Activate ... -func (r RedstoneRepeater) Activate(pos cube.Pos, _ cube.Face, w *world.World, u item.User, _ *item.UseContext) bool { +func (r RedstoneRepeater) Activate(pos cube.Pos, _ cube.Face, w *world.World, _ item.User, _ *item.UseContext) bool { if r.Delay++; r.Delay > 3 { r.Delay = 0 } @@ -110,8 +110,8 @@ func (r RedstoneRepeater) ScheduledTick(pos cube.Pos, w *world.World, _ *rand.Ra } r.Powered = !r.Powered - w.SetBlock(pos, r, &world.SetOpts{DisableBlockUpdates: true}) - updateSurroundingRedstone(pos, w) + w.SetBlock(pos, r, nil) + updateGateRedstone(pos, w, r.Facing.Face().Opposite()) if r.Powered && r.inputStrength(pos, w) <= 0 { w.ScheduleBlockUpdate(pos, time.Duration(r.Delay+1)*time.Millisecond*100) diff --git a/server/block/redstone_torch.go b/server/block/redstone_torch.go index 04fa29779..08c46422b 100644 --- a/server/block/redstone_torch.go +++ b/server/block/redstone_torch.go @@ -37,7 +37,7 @@ func (t RedstoneTorch) LightEmissionLevel() uint8 { // BreakInfo ... func (t RedstoneTorch) BreakInfo() BreakInfo { return newBreakInfo(0, alwaysHarvestable, nothingEffective, oneOf(t)).withBreakHandler(func(pos cube.Pos, w *world.World, _ item.User) { - updateSurroundingRedstone(pos, w) + updateRedstoneWires(pos, w) }) } @@ -71,7 +71,7 @@ func (t RedstoneTorch) UseOnBlock(pos cube.Pos, face cube.Face, _ mgl64.Vec3, w place(w, pos, t, user, ctx) if placed(ctx) { - updateSurroundingRedstone(pos, w) + updateRedstoneWires(pos, w) return true } return false @@ -99,7 +99,7 @@ func (t RedstoneTorch) RedstoneUpdate(pos cube.Pos, w *world.World) { func (t RedstoneTorch) ScheduledTick(pos cube.Pos, w *world.World, _ *rand.Rand) { t.Lit = !t.Lit w.SetBlock(pos, t, &world.SetOpts{DisableBlockUpdates: true}) - updateSurroundingRedstone(pos, w) + updateRedstoneWires(pos, w) } // EncodeItem ... diff --git a/server/block/redstone_wire.go b/server/block/redstone_wire.go index 54c993979..0953934eb 100644 --- a/server/block/redstone_wire.go +++ b/server/block/redstone_wire.go @@ -28,7 +28,7 @@ func (RedstoneWire) HasLiquidDrops() bool { // BreakInfo ... func (r RedstoneWire) BreakInfo() BreakInfo { return newBreakInfo(0, alwaysHarvestable, nothingEffective, oneOf(r)).withBreakHandler(func(pos cube.Pos, w *world.World, _ item.User) { - updateSurroundingRedstone(pos, w) + updateRedstoneWires(pos, w) }) } @@ -57,7 +57,7 @@ func (r RedstoneWire) UseOnBlock(pos cube.Pos, face cube.Face, _ mgl64.Vec3, w * r.Power = r.calculatePower(pos, w) place(w, pos, r, user, ctx) if placed(ctx) { - updateSurroundingRedstone(pos, w) + updateRedstoneWires(pos, w) return true } return false @@ -73,7 +73,7 @@ func (r RedstoneWire) NeighbourUpdateTick(pos, _ cube.Pos, w *world.World) { if power := r.calculatePower(pos, w); r.Power != power { r.Power = power w.SetBlock(pos, r, &world.SetOpts{DisableBlockUpdates: true}) - updateSurroundingRedstone(pos, w) + updateRedstoneWires(pos, w) } } From d1e18f246791c886fb170dba9f461238bd21df98 Mon Sep 17 00:00:00 2001 From: JustTal Date: Wed, 18 Jan 2023 19:34:26 -0600 Subject: [PATCH 23/83] block/redstone.go: ok it actually works !! --- server/block/redstone.go | 40 ++++++++++++++++++++++------------ server/block/redstone_torch.go | 8 +++---- server/block/redstone_wire.go | 6 ++--- 3 files changed, 33 insertions(+), 21 deletions(-) diff --git a/server/block/redstone.go b/server/block/redstone.go index 3b846861a..f33209ab5 100644 --- a/server/block/redstone.go +++ b/server/block/redstone.go @@ -4,6 +4,7 @@ import ( "github.com/df-mc/dragonfly/server/block/cube" "github.com/df-mc/dragonfly/server/block/model" "github.com/df-mc/dragonfly/server/world" + "golang.org/x/exp/slices" ) // RedstoneUpdater represents a block that can be updated through a change in redstone signal. @@ -14,7 +15,7 @@ type RedstoneUpdater interface { // wireNetwork implements a minimally-invasive bolt-on accelerator that performs a breadth-first search through redstone // wires in order to more efficiently and compute new redstone wire power levels and determine the order in which other -// blocks should be updated. This implementation is heavily based off of RedstoneWireTurbo and MCHPRS. +// blocks should be updated. This implementation is heavily based off of RedstoneWireTurbo, RedstoneCircuit, and MCHPRS. type wireNetwork struct { nodes []*wireNode nodeCache map[cube.Pos]*wireNode @@ -45,9 +46,9 @@ const ( wireHeadingWest = 3 ) -// updateRedstoneWires sets off the breadth-first walk through all redstone wires connected to the initial position +// updateStrongRedstone sets off the breadth-first walk through all redstone wires connected to the initial position // triggered. This is the main entry point for the redstone update algorithm. -func updateRedstoneWires(pos cube.Pos, w *world.World) { +func updateStrongRedstone(pos cube.Pos, w *world.World) { n := &wireNetwork{ nodeCache: make(map[cube.Pos]*wireNode), updateQueue: [3][]*wireNode{}, @@ -65,6 +66,27 @@ func updateRedstoneWires(pos cube.Pos, w *world.World) { n.breadthFirstWalk(w) } +// updateAroundRedstone updates redstone components around the given centre position. It will also ignore any faces +// provided within the ignoredFaces parameter. +func updateAroundRedstone(centre cube.Pos, w *world.World, ignoredFaces ...cube.Face) { + for _, face := range cube.Faces() { + if slices.Contains(ignoredFaces, face) { + continue + } + + pos := centre.Side(face) + if r, ok := w.Block(pos).(RedstoneUpdater); ok { + r.RedstoneUpdate(pos, w) + } + } +} + +// updateDirectionalRedstone updates redstone components through the given face. +func updateDirectionalRedstone(pos cube.Pos, w *world.World, face cube.Face) { + updateAroundRedstone(pos, w) + updateAroundRedstone(pos.Side(face), w, face.Opposite()) +} + // updateGateRedstone is used to update redstone gates on each face of the given offset centre position. func updateGateRedstone(centre cube.Pos, w *world.World, face cube.Face) { pos := centre.Side(face) @@ -72,17 +94,7 @@ func updateGateRedstone(centre cube.Pos, w *world.World, face cube.Face) { r.RedstoneUpdate(pos, w) } - opposite := face.Opposite() - for _, f := range cube.Faces() { - if f == opposite { - continue - } - - sidePos := pos.Side(f) - if r, ok := w.Block(sidePos).(RedstoneUpdater); ok { - r.RedstoneUpdate(sidePos, w) - } - } + updateAroundRedstone(pos, w, face.Opposite()) } // identifyNeighbours identifies the neighbouring positions of a given node, determines their types, and links them into diff --git a/server/block/redstone_torch.go b/server/block/redstone_torch.go index 08c46422b..0266e1f4f 100644 --- a/server/block/redstone_torch.go +++ b/server/block/redstone_torch.go @@ -37,7 +37,7 @@ func (t RedstoneTorch) LightEmissionLevel() uint8 { // BreakInfo ... func (t RedstoneTorch) BreakInfo() BreakInfo { return newBreakInfo(0, alwaysHarvestable, nothingEffective, oneOf(t)).withBreakHandler(func(pos cube.Pos, w *world.World, _ item.User) { - updateRedstoneWires(pos, w) + updateDirectionalRedstone(pos, w, cube.FaceUp) }) } @@ -71,7 +71,7 @@ func (t RedstoneTorch) UseOnBlock(pos cube.Pos, face cube.Face, _ mgl64.Vec3, w place(w, pos, t, user, ctx) if placed(ctx) { - updateRedstoneWires(pos, w) + updateDirectionalRedstone(pos, w, cube.FaceUp) return true } return false @@ -98,8 +98,8 @@ func (t RedstoneTorch) RedstoneUpdate(pos cube.Pos, w *world.World) { // ScheduledTick ... func (t RedstoneTorch) ScheduledTick(pos cube.Pos, w *world.World, _ *rand.Rand) { t.Lit = !t.Lit - w.SetBlock(pos, t, &world.SetOpts{DisableBlockUpdates: true}) - updateRedstoneWires(pos, w) + w.SetBlock(pos, t, nil) + updateDirectionalRedstone(pos, w, cube.FaceUp) } // EncodeItem ... diff --git a/server/block/redstone_wire.go b/server/block/redstone_wire.go index 0953934eb..94b6a39a8 100644 --- a/server/block/redstone_wire.go +++ b/server/block/redstone_wire.go @@ -28,7 +28,7 @@ func (RedstoneWire) HasLiquidDrops() bool { // BreakInfo ... func (r RedstoneWire) BreakInfo() BreakInfo { return newBreakInfo(0, alwaysHarvestable, nothingEffective, oneOf(r)).withBreakHandler(func(pos cube.Pos, w *world.World, _ item.User) { - updateRedstoneWires(pos, w) + updateStrongRedstone(pos, w) }) } @@ -57,7 +57,7 @@ func (r RedstoneWire) UseOnBlock(pos cube.Pos, face cube.Face, _ mgl64.Vec3, w * r.Power = r.calculatePower(pos, w) place(w, pos, r, user, ctx) if placed(ctx) { - updateRedstoneWires(pos, w) + updateStrongRedstone(pos, w) return true } return false @@ -73,7 +73,7 @@ func (r RedstoneWire) NeighbourUpdateTick(pos, _ cube.Pos, w *world.World) { if power := r.calculatePower(pos, w); r.Power != power { r.Power = power w.SetBlock(pos, r, &world.SetOpts{DisableBlockUpdates: true}) - updateRedstoneWires(pos, w) + updateStrongRedstone(pos, w) } } From 88ad785288fadea2b284b1b0b07f68233e6a6194 Mon Sep 17 00:00:00 2001 From: JustTal Date: Wed, 18 Jan 2023 21:14:09 -0600 Subject: [PATCH 24/83] block/redstone.go: Use the correct update order + pass correct face into updateDirectionalRedstone. --- server/block/redstone.go | 16 ++++++++++++---- server/block/redstone_torch.go | 6 +++--- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/server/block/redstone.go b/server/block/redstone.go index f33209ab5..0f05d5a98 100644 --- a/server/block/redstone.go +++ b/server/block/redstone.go @@ -15,7 +15,7 @@ type RedstoneUpdater interface { // wireNetwork implements a minimally-invasive bolt-on accelerator that performs a breadth-first search through redstone // wires in order to more efficiently and compute new redstone wire power levels and determine the order in which other -// blocks should be updated. This implementation is heavily based off of RedstoneWireTurbo, RedstoneCircuit, and MCHPRS. +// blocks should be updated. This implementation is heavily based off of RedstoneWireTurbo and MCHPRS. type wireNetwork struct { nodes []*wireNode nodeCache map[cube.Pos]*wireNode @@ -67,9 +67,16 @@ func updateStrongRedstone(pos cube.Pos, w *world.World) { } // updateAroundRedstone updates redstone components around the given centre position. It will also ignore any faces -// provided within the ignoredFaces parameter. +// provided within the ignoredFaces parameter. This implementation is based off of RedstoneCircuit and Java 1.19. func updateAroundRedstone(centre cube.Pos, w *world.World, ignoredFaces ...cube.Face) { - for _, face := range cube.Faces() { + for _, face := range []cube.Face{ + cube.FaceWest, + cube.FaceEast, + cube.FaceDown, + cube.FaceUp, + cube.FaceNorth, + cube.FaceSouth, + } { if slices.Contains(ignoredFaces, face) { continue } @@ -81,7 +88,8 @@ func updateAroundRedstone(centre cube.Pos, w *world.World, ignoredFaces ...cube. } } -// updateDirectionalRedstone updates redstone components through the given face. +// updateDirectionalRedstone updates redstone components through the given face. This implementation is based off of +// RedstoneCircuit and Java 1.19. func updateDirectionalRedstone(pos cube.Pos, w *world.World, face cube.Face) { updateAroundRedstone(pos, w) updateAroundRedstone(pos.Side(face), w, face.Opposite()) diff --git a/server/block/redstone_torch.go b/server/block/redstone_torch.go index 0266e1f4f..28d9c507e 100644 --- a/server/block/redstone_torch.go +++ b/server/block/redstone_torch.go @@ -37,7 +37,7 @@ func (t RedstoneTorch) LightEmissionLevel() uint8 { // BreakInfo ... func (t RedstoneTorch) BreakInfo() BreakInfo { return newBreakInfo(0, alwaysHarvestable, nothingEffective, oneOf(t)).withBreakHandler(func(pos cube.Pos, w *world.World, _ item.User) { - updateDirectionalRedstone(pos, w, cube.FaceUp) + updateDirectionalRedstone(pos, w, t.Facing.Opposite()) }) } @@ -71,7 +71,7 @@ func (t RedstoneTorch) UseOnBlock(pos cube.Pos, face cube.Face, _ mgl64.Vec3, w place(w, pos, t, user, ctx) if placed(ctx) { - updateDirectionalRedstone(pos, w, cube.FaceUp) + updateDirectionalRedstone(pos, w, t.Facing.Opposite()) return true } return false @@ -99,7 +99,7 @@ func (t RedstoneTorch) RedstoneUpdate(pos cube.Pos, w *world.World) { func (t RedstoneTorch) ScheduledTick(pos cube.Pos, w *world.World, _ *rand.Rand) { t.Lit = !t.Lit w.SetBlock(pos, t, nil) - updateDirectionalRedstone(pos, w, cube.FaceUp) + updateDirectionalRedstone(pos, w, t.Facing.Opposite()) } // EncodeItem ... From 89decae62a92ecfdbb93f2e112f3c2212a9e6cf8 Mon Sep 17 00:00:00 2001 From: JustTal Date: Wed, 18 Jan 2023 21:34:47 -0600 Subject: [PATCH 25/83] block/redstone_wire.go: Implement RedstoneUpdater. --- server/block/redstone_wire.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/server/block/redstone_wire.go b/server/block/redstone_wire.go index 94b6a39a8..bd2ae2160 100644 --- a/server/block/redstone_wire.go +++ b/server/block/redstone_wire.go @@ -70,6 +70,11 @@ func (r RedstoneWire) NeighbourUpdateTick(pos, _ cube.Pos, w *world.World) { dropItem(w, item.NewStack(r, 1), pos.Vec3Centre()) return } + r.RedstoneUpdate(pos, w) +} + +// RedstoneUpdate ... +func (r RedstoneWire) RedstoneUpdate(pos cube.Pos, w *world.World) { if power := r.calculatePower(pos, w); r.Power != power { r.Power = power w.SetBlock(pos, r, &world.SetOpts{DisableBlockUpdates: true}) From e85fc68db0d5b5130ed76357c24157eeb5c8abc9 Mon Sep 17 00:00:00 2001 From: JustTal Date: Thu, 19 Jan 2023 23:09:58 -0600 Subject: [PATCH 26/83] block: Various improvements to redstone, also implemented nonfunctional comparators. --- server/block/hash.go | 5 ++ server/block/redstone_block.go | 37 ++++++++-- server/block/redstone_comparator.go | 106 ++++++++++++++++++++++++++++ server/block/redstone_lamp.go | 5 -- server/block/redstone_repeater.go | 10 +-- server/block/redstone_torch.go | 13 +++- server/block/redstone_wire.go | 6 +- server/block/register.go | 2 + server/world/block_state.go | 2 +- 9 files changed, 166 insertions(+), 20 deletions(-) create mode 100644 server/block/redstone_comparator.go diff --git a/server/block/hash.go b/server/block/hash.go index ebf105336..84a9d68d4 100644 --- a/server/block/hash.go +++ b/server/block/hash.go @@ -131,6 +131,7 @@ const ( hashRawGold hashRawIron hashRedstoneBlock + hashRedstoneComparator hashRedstoneLamp hashRedstoneRepeater hashRedstoneTorch @@ -687,6 +688,10 @@ func (RedstoneBlock) Hash() uint64 { return hashRedstoneBlock } +func (r RedstoneComparator) Hash() uint64 { + return hashRedstoneComparator | uint64(r.Facing)<<8 | uint64(boolByte(r.Subtract))<<10 | uint64(boolByte(r.Powered))<<11 | uint64(r.Power)<<12 +} + func (l RedstoneLamp) Hash() uint64 { return hashRedstoneLamp | uint64(boolByte(l.Lit))<<8 } diff --git a/server/block/redstone_block.go b/server/block/redstone_block.go index 537d257f1..37443cee3 100644 --- a/server/block/redstone_block.go +++ b/server/block/redstone_block.go @@ -2,32 +2,57 @@ package block import ( "github.com/df-mc/dragonfly/server/block/cube" + "github.com/df-mc/dragonfly/server/item" "github.com/df-mc/dragonfly/server/world" + "github.com/go-gl/mathgl/mgl64" ) -type RedstoneBlock struct{ solid } +type RedstoneBlock struct { + solid +} + +// BreakInfo ... +func (r RedstoneBlock) BreakInfo() BreakInfo { + return newBreakInfo(5, pickaxeHarvestable, pickaxeEffective, oneOf(r)).withBreakHandler(func(pos cube.Pos, w *world.World, _ item.User) { + updateAroundRedstone(pos, w) + }) +} // EncodeItem ... -func (b RedstoneBlock) EncodeItem() (name string, meta int16) { +func (r RedstoneBlock) EncodeItem() (name string, meta int16) { return "minecraft:redstone_block", 0 } // EncodeBlock ... -func (b RedstoneBlock) EncodeBlock() (string, map[string]any) { +func (r RedstoneBlock) EncodeBlock() (string, map[string]any) { return "minecraft:redstone_block", nil } +// UseOnBlock ... +func (r RedstoneBlock) UseOnBlock(pos cube.Pos, face cube.Face, _ mgl64.Vec3, w *world.World, user item.User, ctx *item.UseContext) bool { + pos, _, used := firstReplaceable(w, pos, face, r) + if !used { + return false + } + place(w, pos, r, user, ctx) + if placed(ctx) { + updateAroundRedstone(pos, w) + return true + } + return false +} + // Source ... -func (b RedstoneBlock) Source() bool { +func (r RedstoneBlock) Source() bool { return true } // WeakPower ... -func (b RedstoneBlock) WeakPower(cube.Pos, cube.Face, *world.World, bool) int { +func (r RedstoneBlock) WeakPower(cube.Pos, cube.Face, *world.World, bool) int { return 15 } // StrongPower ... -func (b RedstoneBlock) StrongPower(cube.Pos, cube.Face, *world.World, bool) int { +func (r RedstoneBlock) StrongPower(cube.Pos, cube.Face, *world.World, bool) int { return 0 } diff --git a/server/block/redstone_comparator.go b/server/block/redstone_comparator.go new file mode 100644 index 000000000..d3bc37589 --- /dev/null +++ b/server/block/redstone_comparator.go @@ -0,0 +1,106 @@ +package block + +import ( + "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/go-gl/mathgl/mgl64" +) + +// RedstoneComparator is a redstone component used to maintain, compare, or subtract signal strength, or to measure +// certain block states (primarily the fullness of containers). +type RedstoneComparator struct { + transparent + + // Facing is the direction from the torch to the block. + Facing cube.Direction + // Subtract is true if the comparator is in subtract mode. + Subtract bool + // Powered is true if the repeater is powered by a redstone signal. + Powered bool + // Power is the current power level of the redstone comparator. It ranges from 0 to 15. + Power int +} + +// HasLiquidDrops ... +func (RedstoneComparator) HasLiquidDrops() bool { + return true +} + +// Model ... +func (RedstoneComparator) Model() world.BlockModel { + return model.Diode{} +} + +// EncodeItem ... +func (RedstoneComparator) EncodeItem() (name string, meta int16) { + return "minecraft:comparator", 0 +} + +// EncodeBlock ... +func (r RedstoneComparator) EncodeBlock() (string, map[string]any) { + name := "minecraft:unpowered_comparator" + if r.Powered { + name = "minecraft:powered_comparator" + } + return name, map[string]any{ + "direction": int32(horizontalDirection(r.Facing)), + "output_lit_bit": boolByte(r.Powered), + "output_subtract_bit": boolByte(r.Subtract), + } +} + +// UseOnBlock ... +func (r RedstoneComparator) UseOnBlock(pos cube.Pos, face cube.Face, _ mgl64.Vec3, w *world.World, user item.User, ctx *item.UseContext) bool { + pos, _, used := firstReplaceable(w, pos, face, r) + if !used { + return false + } + if d, ok := w.Block(pos.Side(cube.FaceDown)).(LightDiffuser); ok && d.LightDiffusionLevel() == 0 { + return false + } + r.Facing = user.Rotation().Direction().Opposite() + + place(w, pos, r, user, ctx) + return placed(ctx) +} + +// NeighbourUpdateTick ... +func (r RedstoneComparator) NeighbourUpdateTick(pos, _ cube.Pos, w *world.World) { + if d, ok := w.Block(pos.Side(cube.FaceDown)).(LightDiffuser); ok && d.LightDiffusionLevel() == 0 { + w.SetBlock(pos, nil, nil) + dropItem(w, item.NewStack(r, 1), pos.Vec3Centre()) + } +} + +// Activate ... +func (r RedstoneComparator) Activate(pos cube.Pos, _ cube.Face, w *world.World, _ item.User, _ *item.UseContext) bool { + r.Subtract = !r.Subtract + w.SetBlock(pos, r, nil) + return false +} + +// EncodeNBT ... +func (r RedstoneComparator) EncodeNBT() map[string]any { + return map[string]any{"OutputSignal": int32(r.Power)} +} + +// DecodeNBT ... +func (r RedstoneComparator) DecodeNBT(data map[string]any) any { + r.Power = int(nbtconv.Int32(data, "OutputSignal")) + return r +} + +// allRedstoneComparators ... +func allRedstoneComparators() (comparators []world.Block) { + for _, d := range cube.Directions() { + for _, s := range []bool{false, true} { + for _, p := range []bool{false, true} { + comparators = append(comparators, RedstoneComparator{Facing: d, Subtract: s, Powered: p}) + } + } + } + return +} diff --git a/server/block/redstone_lamp.go b/server/block/redstone_lamp.go index 327d6b18d..f4e2a07c7 100644 --- a/server/block/redstone_lamp.go +++ b/server/block/redstone_lamp.go @@ -54,11 +54,6 @@ func (l RedstoneLamp) UseOnBlock(pos cube.Pos, face cube.Face, _ mgl64.Vec3, w * return placed(ctx) } -// NeighbourUpdateTick ... -func (l RedstoneLamp) NeighbourUpdateTick(pos, _ cube.Pos, w *world.World) { - l.RedstoneUpdate(pos, w) -} - // RedstoneUpdate ... func (l RedstoneLamp) RedstoneUpdate(pos cube.Pos, w *world.World) { if l.Lit == l.receivedRedstonePower(pos, w) { diff --git a/server/block/redstone_repeater.go b/server/block/redstone_repeater.go index e1b2c7a86..6545e4fbb 100644 --- a/server/block/redstone_repeater.go +++ b/server/block/redstone_repeater.go @@ -47,10 +47,14 @@ func (RedstoneRepeater) EncodeItem() (name string, meta int16) { // EncodeBlock ... func (r RedstoneRepeater) EncodeBlock() (string, map[string]any) { + name := "minecraft:unpowered_repeater" if r.Powered { - return "minecraft:powered_repeater", map[string]any{"direction": int32(horizontalDirection(r.Facing)), "repeater_delay": int32(r.Delay)} + name = "minecraft:powered_repeater" + } + return name, map[string]any{ + "direction": int32(horizontalDirection(r.Facing)), + "repeater_delay": int32(r.Delay), } - return "minecraft:unpowered_repeater", map[string]any{"direction": int32(horizontalDirection(r.Facing)), "repeater_delay": int32(r.Delay)} } // UseOnBlock ... @@ -77,9 +81,7 @@ func (r RedstoneRepeater) NeighbourUpdateTick(pos, _ cube.Pos, w *world.World) { if d, ok := w.Block(pos.Side(cube.FaceDown)).(LightDiffuser); ok && d.LightDiffusionLevel() == 0 { w.SetBlock(pos, nil, nil) dropItem(w, item.NewStack(r, 1), pos.Vec3Centre()) - return } - r.RedstoneUpdate(pos, w) } // Activate ... diff --git a/server/block/redstone_torch.go b/server/block/redstone_torch.go index 28d9c507e..5f4baa664 100644 --- a/server/block/redstone_torch.go +++ b/server/block/redstone_torch.go @@ -71,6 +71,7 @@ func (t RedstoneTorch) UseOnBlock(pos cube.Pos, face cube.Face, _ mgl64.Vec3, w place(w, pos, t, user, ctx) if placed(ctx) { + t.RedstoneUpdate(pos, w) updateDirectionalRedstone(pos, w, t.Facing.Opposite()) return true } @@ -82,14 +83,12 @@ func (t RedstoneTorch) NeighbourUpdateTick(pos, _ cube.Pos, w *world.World) { if !w.Block(pos.Side(t.Facing)).Model().FaceSolid(pos.Side(t.Facing), t.Facing.Opposite(), w) { w.SetBlock(pos, nil, nil) dropItem(w, item.NewStack(t, 1), pos.Vec3Centre()) - return } - t.RedstoneUpdate(pos, w) } // RedstoneUpdate ... func (t RedstoneTorch) RedstoneUpdate(pos cube.Pos, w *world.World) { - if w.EmittedRedstonePower(pos.Side(t.Facing), t.Facing, true) > 0 != t.Lit { + if t.inputStrength(pos, w) > 0 != t.Lit { return } w.ScheduleBlockUpdate(pos, time.Millisecond*100) @@ -97,6 +96,9 @@ func (t RedstoneTorch) RedstoneUpdate(pos cube.Pos, w *world.World) { // ScheduledTick ... func (t RedstoneTorch) ScheduledTick(pos cube.Pos, w *world.World, _ *rand.Rand) { + if t.inputStrength(pos, w) > 0 != t.Lit { + return + } t.Lit = !t.Lit w.SetBlock(pos, t, nil) updateDirectionalRedstone(pos, w, t.Facing.Opposite()) @@ -152,6 +154,11 @@ func (t RedstoneTorch) StrongPower(_ cube.Pos, face cube.Face, _ *world.World, _ return 0 } +// inputStrength ... +func (t RedstoneTorch) inputStrength(pos cube.Pos, w *world.World) int { + return w.EmittedRedstonePower(pos.Side(t.Facing), t.Facing, true) +} + // allRedstoneTorches ... func allRedstoneTorches() (all []world.Block) { for _, f := range append(cube.Faces(), unknownFace) { diff --git a/server/block/redstone_wire.go b/server/block/redstone_wire.go index bd2ae2160..082e4545f 100644 --- a/server/block/redstone_wire.go +++ b/server/block/redstone_wire.go @@ -64,7 +64,11 @@ func (r RedstoneWire) UseOnBlock(pos cube.Pos, face cube.Face, _ mgl64.Vec3, w * } // NeighbourUpdateTick ... -func (r RedstoneWire) NeighbourUpdateTick(pos, _ cube.Pos, w *world.World) { +func (r RedstoneWire) NeighbourUpdateTick(pos, neighbour cube.Pos, w *world.World) { + if pos == neighbour { + // Ignore neighbour updates on ourself. + return + } if _, ok := w.Block(pos.Side(cube.FaceDown)).(Air); ok { w.SetBlock(pos, nil, nil) dropItem(w, item.NewStack(r, 1), pos.Vec3Centre()) diff --git a/server/block/register.go b/server/block/register.go index 4838ba1a8..1de98ff53 100644 --- a/server/block/register.go +++ b/server/block/register.go @@ -171,6 +171,7 @@ func init() { registerAll(allPumpkins()) registerAll(allPurpurs()) registerAll(allQuartz()) + registerAll(allRedstoneComparators()) registerAll(allRedstoneRepeaters()) registerAll(allRedstoneTorches()) registerAll(allRedstoneWires()) @@ -309,6 +310,7 @@ func init() { world.RegisterItem(RawGold{}) world.RegisterItem(RawIron{}) world.RegisterItem(RedstoneBlock{}) + world.RegisterItem(RedstoneComparator{}) world.RegisterItem(RedstoneLamp{}) world.RegisterItem(RedstoneRepeater{}) world.RegisterItem(RedstoneTorch{}) diff --git a/server/world/block_state.go b/server/world/block_state.go index 82aed966c..5d8f4506b 100644 --- a/server/world/block_state.go +++ b/server/world/block_state.go @@ -66,7 +66,7 @@ func init() { // registerBlockState registers a new blockState to the states slice. The function panics if the properties the // blockState hold are invalid or if the blockState was already registered. func registerBlockState(s blockState) { - if strings.Contains(s.Name, "repeater") { + if strings.Contains(s.Name, "comparator") { pretty.Println(s) } From f163f7a2ec9f68bd4feb1b1eda66070873b1ec7f Mon Sep 17 00:00:00 2001 From: DaPigGuy Date: Thu, 19 Jan 2023 21:30:23 -0800 Subject: [PATCH 27/83] block: removed unused var --- server/block/block.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/server/block/block.go b/server/block/block.go index 1c275ede0..e3846d14b 100644 --- a/server/block/block.go +++ b/server/block/block.go @@ -81,8 +81,6 @@ type Frictional interface { } var ( - // unknownDirection is a direction that is used for certain block items. This should not be exposed in the API. - unknownDirection = cube.Direction(len(cube.Directions())) // unknownFace is a face that is used for certain block items. This should not be exposed in the API. unknownFace = cube.Face(len(cube.Faces())) ) From b41f4d5fbd966cca9ccac57b1bf5530ef0b4f1fb Mon Sep 17 00:00:00 2001 From: JustTal Date: Fri, 20 Jan 2023 11:14:47 -0600 Subject: [PATCH 28/83] main.go: Run a pprof HTTP server. --- main.go | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/main.go b/main.go index 7b0581d9b..59849e8a8 100644 --- a/main.go +++ b/main.go @@ -6,18 +6,26 @@ import ( "github.com/df-mc/dragonfly/server/block" "github.com/df-mc/dragonfly/server/block/cube" "github.com/df-mc/dragonfly/server/block/cube/trace" - "github.com/df-mc/dragonfly/server/item" "github.com/df-mc/dragonfly/server/player" "github.com/df-mc/dragonfly/server/player/chat" "github.com/df-mc/dragonfly/server/world" "github.com/go-gl/mathgl/mgl64" "github.com/pelletier/go-toml" "github.com/sirupsen/logrus" + "net/http" + _ "net/http/pprof" "os" "time" ) func main() { + go func() { + err := http.ListenAndServe(":6060", nil) + if err != nil { + panic(err) + } + }() + log := logrus.New() log.Formatter = &logrus.TextFormatter{ForceColors: true} log.Level = logrus.DebugLevel @@ -35,7 +43,6 @@ func main() { srv.Listen() for srv.Accept(func(p *player.Player) { p.Handle(newRedstonePlayerHandler(p)) - p.Inventory().AddItem(item.NewStack(block.Torch{}, 1)) }) { } } From eb8bdfa1883b6336fe4db7e561c85d737cd5f783 Mon Sep 17 00:00:00 2001 From: JustTal Date: Fri, 20 Jan 2023 15:47:38 -0600 Subject: [PATCH 29/83] block/redstone_repeater.go: somewhat fix jittering? --- server/block/redstone_repeater.go | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/server/block/redstone_repeater.go b/server/block/redstone_repeater.go index 6545e4fbb..97fe48049 100644 --- a/server/block/redstone_repeater.go +++ b/server/block/redstone_repeater.go @@ -110,13 +110,17 @@ func (r RedstoneRepeater) ScheduledTick(pos cube.Pos, w *world.World, _ *rand.Ra // Ignore this tick; the repeater is locked. return } - - r.Powered = !r.Powered - w.SetBlock(pos, r, nil) - updateGateRedstone(pos, w, r.Facing.Face().Opposite()) - - if r.Powered && r.inputStrength(pos, w) <= 0 { - w.ScheduleBlockUpdate(pos, time.Duration(r.Delay+1)*time.Millisecond*100) + if shouldBePowered := r.inputStrength(pos, w) > 0; r.Powered && !shouldBePowered { + r.Powered = false + w.SetBlock(pos, r, nil) + updateGateRedstone(pos, w, r.Facing.Face().Opposite()) + } else if !r.Powered { + r.Powered = true + w.SetBlock(pos, r, nil) + updateGateRedstone(pos, w, r.Facing.Face().Opposite()) + if !shouldBePowered { + w.ScheduleBlockUpdate(pos, time.Duration(r.Delay+1)*time.Millisecond*100) + } } } @@ -146,14 +150,14 @@ func (r RedstoneRepeater) StrongPower(pos cube.Pos, face cube.Face, w *world.Wor // inputStrength ... func (r RedstoneRepeater) inputStrength(pos cube.Pos, w *world.World) int { - sidePos := pos.Side(r.Facing.Face()) - if strength := w.EmittedRedstonePower(sidePos, r.Facing.Face(), true); strength > 0 { - return strength - } + face := r.Facing.Face() + sidePos := pos.Side(face) + + strength := w.EmittedRedstonePower(sidePos, face, true) if w, ok := w.Block(sidePos).(RedstoneWire); ok { - return w.Power + return max(strength, w.Power) } - return 0 + return strength } // allRedstoneRepeaters ... From c039fd268371a55e46fffc3976a25da12802e029 Mon Sep 17 00:00:00 2001 From: JustTal Date: Fri, 20 Jan 2023 16:14:08 -0600 Subject: [PATCH 30/83] block/redstone.go: i think this is it? --- server/block/redstone.go | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/server/block/redstone.go b/server/block/redstone.go index 0f05d5a98..1b2e7b9ec 100644 --- a/server/block/redstone.go +++ b/server/block/redstone.go @@ -97,12 +97,7 @@ func updateDirectionalRedstone(pos cube.Pos, w *world.World, face cube.Face) { // updateGateRedstone is used to update redstone gates on each face of the given offset centre position. func updateGateRedstone(centre cube.Pos, w *world.World, face cube.Face) { - pos := centre.Side(face) - if r, ok := w.Block(pos).(RedstoneUpdater); ok { - r.RedstoneUpdate(pos, w) - } - - updateAroundRedstone(pos, w, face.Opposite()) + updateAroundRedstone(centre.Side(face), w, face.Opposite()) } // identifyNeighbours identifies the neighbouring positions of a given node, determines their types, and links them into From 44e17933db7a1df0b9810974012b7d317619085e Mon Sep 17 00:00:00 2001 From: JustTal Date: Fri, 20 Jan 2023 16:18:35 -0600 Subject: [PATCH 31/83] block/redstone.go: revert tests --- server/block/redstone.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/server/block/redstone.go b/server/block/redstone.go index 1b2e7b9ec..0f05d5a98 100644 --- a/server/block/redstone.go +++ b/server/block/redstone.go @@ -97,7 +97,12 @@ func updateDirectionalRedstone(pos cube.Pos, w *world.World, face cube.Face) { // updateGateRedstone is used to update redstone gates on each face of the given offset centre position. func updateGateRedstone(centre cube.Pos, w *world.World, face cube.Face) { - updateAroundRedstone(centre.Side(face), w, face.Opposite()) + pos := centre.Side(face) + if r, ok := w.Block(pos).(RedstoneUpdater); ok { + r.RedstoneUpdate(pos, w) + } + + updateAroundRedstone(pos, w, face.Opposite()) } // identifyNeighbours identifies the neighbouring positions of a given node, determines their types, and links them into From acaf073f05b32067e366b449fd04e46d20c665d2 Mon Sep 17 00:00:00 2001 From: JustTal Date: Fri, 20 Jan 2023 16:59:50 -0600 Subject: [PATCH 32/83] block/redstone.go: reee --- server/block/redstone.go | 4 ++-- server/block/redstone_repeater.go | 8 +++----- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/server/block/redstone.go b/server/block/redstone.go index 0f05d5a98..cdad82603 100644 --- a/server/block/redstone.go +++ b/server/block/redstone.go @@ -97,12 +97,12 @@ func updateDirectionalRedstone(pos cube.Pos, w *world.World, face cube.Face) { // updateGateRedstone is used to update redstone gates on each face of the given offset centre position. func updateGateRedstone(centre cube.Pos, w *world.World, face cube.Face) { - pos := centre.Side(face) + pos := centre.Side(face.Opposite()) if r, ok := w.Block(pos).(RedstoneUpdater); ok { r.RedstoneUpdate(pos, w) } - updateAroundRedstone(pos, w, face.Opposite()) + updateAroundRedstone(pos, w, face) } // identifyNeighbours identifies the neighbouring positions of a given node, determines their types, and links them into diff --git a/server/block/redstone_repeater.go b/server/block/redstone_repeater.go index 97fe48049..b8989245b 100644 --- a/server/block/redstone_repeater.go +++ b/server/block/redstone_repeater.go @@ -36,7 +36,7 @@ func (RedstoneRepeater) Model() world.BlockModel { // BreakInfo ... func (r RedstoneRepeater) BreakInfo() BreakInfo { return newBreakInfo(0, alwaysHarvestable, nothingEffective, oneOf(r)).withBreakHandler(func(pos cube.Pos, w *world.World, _ item.User) { - updateGateRedstone(pos, w, r.Facing.Face().Opposite()) + updateGateRedstone(pos, w, r.Facing.Face()) }) } @@ -112,16 +112,14 @@ func (r RedstoneRepeater) ScheduledTick(pos cube.Pos, w *world.World, _ *rand.Ra } if shouldBePowered := r.inputStrength(pos, w) > 0; r.Powered && !shouldBePowered { r.Powered = false - w.SetBlock(pos, r, nil) - updateGateRedstone(pos, w, r.Facing.Face().Opposite()) } else if !r.Powered { r.Powered = true - w.SetBlock(pos, r, nil) - updateGateRedstone(pos, w, r.Facing.Face().Opposite()) if !shouldBePowered { w.ScheduleBlockUpdate(pos, time.Duration(r.Delay+1)*time.Millisecond*100) } } + w.SetBlock(pos, r, nil) + updateGateRedstone(pos, w, r.Facing.Face()) } // Locked ... From d21b424248ef522b9192176f50f42ef5daa9cc72 Mon Sep 17 00:00:00 2001 From: JustTal Date: Fri, 20 Jan 2023 22:55:21 -0600 Subject: [PATCH 33/83] world/world.go: Use an orderedmap for scheduled updates. --- go.mod | 10 ++++++++-- go.sum | 17 +++++++++++++++++ server/block/redstone.go | 2 +- server/block/redstone_lamp.go | 2 +- server/block/redstone_repeater.go | 6 +++--- server/block/redstone_torch.go | 2 +- server/block/redstone_wire.go | 10 +++++----- server/world/block.go | 4 ++-- server/world/block_state.go | 5 ----- server/world/conf.go | 3 ++- server/world/tick.go | 10 +++++----- server/world/world.go | 26 +++++++++++--------------- 12 files changed, 56 insertions(+), 41 deletions(-) diff --git a/go.mod b/go.mod index 06ef883fd..cae6d22b1 100644 --- a/go.mod +++ b/go.mod @@ -20,13 +20,19 @@ require ( ) require ( + github.com/bahlo/generic-list-go v0.2.0 // indirect + github.com/buger/jsonparser v1.1.1 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect github.com/golang/protobuf v1.5.2 // indirect github.com/golang/snappy v0.0.4 // indirect github.com/klauspost/compress v1.15.1 // indirect github.com/kr/text v0.1.0 // indirect + github.com/mailru/easyjson v0.7.7 // indirect github.com/muhammadmuzzammil1998/jsonc v1.0.0 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect github.com/sandertv/go-raknet v1.12.0 // indirect - github.com/stretchr/testify v1.7.0 // indirect + github.com/stretchr/testify v1.8.1 // indirect + github.com/wk8/go-ordered-map/v2 v2.1.5 // indirect golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 // indirect golang.org/x/image v0.0.0-20220321031419-a8550c1d254a // indirect golang.org/x/net v0.0.0-20220418201149-a630d4f3e7a2 // indirect @@ -36,5 +42,5 @@ require ( google.golang.org/protobuf v1.28.0 // indirect gopkg.in/square/go-jose.v2 v2.6.0 // indirect gopkg.in/yaml.v2 v2.3.0 // indirect - gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 704b8595e..acebc4827 100644 --- a/go.sum +++ b/go.sum @@ -35,8 +35,12 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03 github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk= +github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg= github.com/brentp/intintmap v0.0.0-20190211203843-30dc0ade9af9 h1:/G0ghZwrhou0Wq21qc1vXXMm/t/aKWkALWwITptKbE0= github.com/brentp/intintmap v0.0.0-20190211203843-30dc0ade9af9/go.mod h1:TOk10ahXejq9wkEaym3KPRNeuR/h5Jx+s8QRWIa2oTM= +github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs= +github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= @@ -122,6 +126,7 @@ github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= @@ -133,6 +138,8 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/muhammadmuzzammil1998/jsonc v1.0.0 h1:8o5gBQn4ZA3NBA9DlTujCj2a4w0tqWrPVjDwhzkgTIs= github.com/muhammadmuzzammil1998/jsonc v1.0.0/go.mod h1:saF2fIVw4banK0H4+/EuqfFLpRnoy5S+ECwTOCcRcSU= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= @@ -178,11 +185,19 @@ github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72 h1:qLC7fQah7D6K1B0ujays3HV9gkFtllcxhzImRR7ArPQ= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/wk8/go-ordered-map/v2 v2.1.5 h1:jLbYIFyWQMUwHLO20cImlCRBoNc5lp0nmE2dvwcxc7k= +github.com/wk8/go-ordered-map/v2 v2.1.5/go.mod h1:9Xvgm2mV2kSq2SAm0Y608tBmu8akTzI7c2bz7/G7ZN4= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -462,6 +477,8 @@ gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/server/block/redstone.go b/server/block/redstone.go index cdad82603..d99d10c43 100644 --- a/server/block/redstone.go +++ b/server/block/redstone.go @@ -278,7 +278,7 @@ func (n *wireNetwork) calculateCurrentChanges(w *world.World, node *wireNode) Re var wirePower int for _, face := range cube.Faces() { - wirePower = max(wirePower, w.EmittedRedstonePower(node.pos.Side(face), face, false)) + wirePower = max(wirePower, w.RedstonePower(node.pos.Side(face), face, false)) } if wirePower < 15 { diff --git a/server/block/redstone_lamp.go b/server/block/redstone_lamp.go index f4e2a07c7..f36668aa9 100644 --- a/server/block/redstone_lamp.go +++ b/server/block/redstone_lamp.go @@ -79,7 +79,7 @@ func (l RedstoneLamp) ScheduledTick(pos cube.Pos, w *world.World, _ *rand.Rand) // receivedRedstonePower ... func (l RedstoneLamp) receivedRedstonePower(pos cube.Pos, w *world.World) bool { for _, face := range cube.Faces() { - if w.EmittedRedstonePower(pos.Side(face), face, true) > 0 { + if w.RedstonePower(pos.Side(face), face, true) > 0 { return true } } diff --git a/server/block/redstone_repeater.go b/server/block/redstone_repeater.go index b8989245b..e45758612 100644 --- a/server/block/redstone_repeater.go +++ b/server/block/redstone_repeater.go @@ -142,8 +142,8 @@ func (r RedstoneRepeater) WeakPower(_ cube.Pos, face cube.Face, _ *world.World, } // StrongPower ... -func (r RedstoneRepeater) StrongPower(pos cube.Pos, face cube.Face, w *world.World, includeDust bool) int { - return r.WeakPower(pos, face, w, includeDust) +func (r RedstoneRepeater) StrongPower(pos cube.Pos, face cube.Face, w *world.World, accountForDust bool) int { + return r.WeakPower(pos, face, w, accountForDust) } // inputStrength ... @@ -151,7 +151,7 @@ func (r RedstoneRepeater) inputStrength(pos cube.Pos, w *world.World) int { face := r.Facing.Face() sidePos := pos.Side(face) - strength := w.EmittedRedstonePower(sidePos, face, true) + strength := w.RedstonePower(sidePos, face, true) if w, ok := w.Block(sidePos).(RedstoneWire); ok { return max(strength, w.Power) } diff --git a/server/block/redstone_torch.go b/server/block/redstone_torch.go index 5f4baa664..305b50c6a 100644 --- a/server/block/redstone_torch.go +++ b/server/block/redstone_torch.go @@ -156,7 +156,7 @@ func (t RedstoneTorch) StrongPower(_ cube.Pos, face cube.Face, _ *world.World, _ // inputStrength ... func (t RedstoneTorch) inputStrength(pos cube.Pos, w *world.World) int { - return w.EmittedRedstonePower(pos.Side(t.Facing), t.Facing, true) + return w.RedstonePower(pos.Side(t.Facing), t.Facing, true) } // allRedstoneTorches ... diff --git a/server/block/redstone_wire.go b/server/block/redstone_wire.go index 082e4545f..13c2405a3 100644 --- a/server/block/redstone_wire.go +++ b/server/block/redstone_wire.go @@ -92,8 +92,8 @@ func (RedstoneWire) Source() bool { } // WeakPower ... -func (r RedstoneWire) WeakPower(pos cube.Pos, face cube.Face, w *world.World, includeDust bool) int { - if !includeDust { +func (r RedstoneWire) WeakPower(pos cube.Pos, face cube.Face, w *world.World, accountForDust bool) int { + if !accountForDust { return 0 } if face == cube.FaceDown { @@ -109,8 +109,8 @@ func (r RedstoneWire) WeakPower(pos cube.Pos, face cube.Face, w *world.World, in } // StrongPower ... -func (r RedstoneWire) StrongPower(pos cube.Pos, face cube.Face, w *world.World, includeDust bool) int { - return r.WeakPower(pos, face, w, includeDust) +func (r RedstoneWire) StrongPower(pos cube.Pos, face cube.Face, w *world.World, accountForDust bool) int { + return r.WeakPower(pos, face, w, accountForDust) } // calculatePower returns the highest level of received redstone power at the provided position. @@ -124,7 +124,7 @@ func (r RedstoneWire) calculatePower(pos cube.Pos, w *world.World) int { neighbour := w.Block(neighbourPos) wirePower = r.maxCurrentStrength(wirePower, neighbourPos, w) - blockPower = max(blockPower, w.EmittedRedstonePower(neighbourPos, side, false)) + blockPower = max(blockPower, w.RedstonePower(neighbourPos, side, false)) if side.Axis() == cube.Y { // Only check horizontal neighbours from here on. diff --git a/server/world/block.go b/server/world/block.go index e3cca3883..851313b60 100644 --- a/server/world/block.go +++ b/server/world/block.go @@ -55,9 +55,9 @@ type Conductor interface { // Source returns true if the conductor is a signal source. Source() bool // WeakPower returns the power from a partial source and has limited usage. - WeakPower(pos cube.Pos, face cube.Face, w *World, includeDust bool) int + WeakPower(pos cube.Pos, face cube.Face, w *World, accountForDust bool) int // StrongPower returns the power from a full source and can be passed to any redstone component. - StrongPower(pos cube.Pos, face cube.Face, w *World, includeDust bool) int + StrongPower(pos cube.Pos, face cube.Face, w *World, accountForDust bool) int } // hashes holds a list of runtime IDs indexed by the hash of the Block that implements the blocks pointed to by those diff --git a/server/world/block_state.go b/server/world/block_state.go index 5d8f4506b..49cd8af6b 100644 --- a/server/world/block_state.go +++ b/server/world/block_state.go @@ -5,7 +5,6 @@ import ( _ "embed" "fmt" "github.com/df-mc/dragonfly/server/world/chunk" - "github.com/kr/pretty" "github.com/sandertv/gophertunnel/minecraft/nbt" "math" "sort" @@ -66,10 +65,6 @@ func init() { // registerBlockState registers a new blockState to the states slice. The function panics if the properties the // blockState hold are invalid or if the blockState was already registered. func registerBlockState(s blockState) { - if strings.Contains(s.Name, "comparator") { - pretty.Println(s) - } - h := stateHash{name: s.Name, properties: hashProperties(s.Properties)} if _, ok := stateRuntimeIDs[h]; ok { panic(fmt.Sprintf("cannot register the same state twice (%+v)", s)) diff --git a/server/world/conf.go b/server/world/conf.go index a6b9ca905..faadf2294 100644 --- a/server/world/conf.go +++ b/server/world/conf.go @@ -4,6 +4,7 @@ import ( "github.com/df-mc/atomic" "github.com/df-mc/dragonfly/server/block/cube" "github.com/sirupsen/logrus" + "github.com/wk8/go-ordered-map/v2" "math/rand" "time" ) @@ -71,7 +72,7 @@ func (conf Config) New() *World { } s := conf.Provider.Settings() w := &World{ - scheduledUpdates: make(map[cube.Pos]int64), + scheduledUpdates: orderedmap.New[cube.Pos, int64](), entities: make(map[Entity]ChunkPos), viewers: make(map[*Loader]Viewer), chunks: make(map[ChunkPos]*chunkData), diff --git a/server/world/tick.go b/server/world/tick.go index fa02ee764..2a09f1f58 100644 --- a/server/world/tick.go +++ b/server/world/tick.go @@ -77,11 +77,11 @@ func (t ticker) tick() { // tickScheduledBlocks executes scheduled block updates in chunks that are currently loaded. func (t ticker) tickScheduledBlocks(tick int64) { t.w.updateMu.Lock() - positions := make([]cube.Pos, 0, len(t.w.scheduledUpdates)/4) - for pos, scheduledTick := range t.w.scheduledUpdates { - if scheduledTick <= tick { - positions = append(positions, pos) - delete(t.w.scheduledUpdates, pos) + positions := make([]cube.Pos, 0, t.w.scheduledUpdates.Len()/4) + for pair := t.w.scheduledUpdates.Oldest(); pair != nil; pair = pair.Next() { + if pair.Value <= tick { + positions = append(positions, pair.Key) + t.w.scheduledUpdates.Delete(pair.Key) } } t.w.updateMu.Unlock() diff --git a/server/world/world.go b/server/world/world.go index 095a6de64..f8b9679e4 100644 --- a/server/world/world.go +++ b/server/world/world.go @@ -2,6 +2,7 @@ package world import ( "fmt" + orderedmap "github.com/wk8/go-ordered-map/v2" "math/rand" "sync" "time" @@ -59,7 +60,7 @@ type World struct { // scheduledUpdates is a map of tick time values indexed by the block position at which an update is // scheduled. If the current tick exceeds the tick value passed, the block update will be performed // and the entry will be removed from the map. - scheduledUpdates map[cube.Pos]int64 + scheduledUpdates *orderedmap.OrderedMap[cube.Pos, int64] neighbourUpdates []neighbourUpdate viewersMu sync.Mutex @@ -928,14 +929,14 @@ func (w *World) ScheduleBlockUpdate(pos cube.Pos, delay time.Duration) { } w.updateMu.Lock() defer w.updateMu.Unlock() - if _, exists := w.scheduledUpdates[pos]; exists { + if _, exists := w.scheduledUpdates.Get(pos); exists { return } w.set.Lock() t := w.set.CurrentTick w.set.Unlock() - w.scheduledUpdates[pos] = t + delay.Nanoseconds()/int64(time.Second/20) + w.scheduledUpdates.Set(pos, t+delay.Nanoseconds()/int64(time.Second/20)) } // doBlockUpdatesAround schedules block updates directly around and on the position passed. @@ -1003,28 +1004,23 @@ func (w *World) PortalDestination(dim Dimension) *World { return w } -// EmittedRedstonePower returns the level of redstone power being emitted from a position to the provided face. -func (w *World) EmittedRedstonePower(pos cube.Pos, face cube.Face, includeDust bool) (emitted int) { +// RedstonePower returns the level of redstone power being emitted from a position to the provided face. +func (w *World) RedstonePower(pos cube.Pos, face cube.Face, accountForDust bool) (power int) { b := w.Block(pos) if c, ok := b.(Conductor); ok { - emitted = c.WeakPower(pos, face, w, includeDust) + power = c.WeakPower(pos, face, w, accountForDust) } for _, f := range cube.Faces() { if !b.Model().FaceSolid(pos, f, w) { - return emitted + return power } } - return max(emitted, w.ReceivedStrongRedstonePower(pos, includeDust)) -} - -// ReceivedStrongRedstonePower returns the level of strong redstone power being received at the provided position. -func (w *World) ReceivedStrongRedstonePower(pos cube.Pos, includeDust bool) (power int) { - for _, face := range cube.Faces() { - c, ok := w.Block(pos.Side(face)).(Conductor) + for _, f := range cube.Faces() { + c, ok := w.Block(pos.Side(f)).(Conductor) if !ok { continue } - power = max(power, c.StrongPower(pos.Side(face), face, w, includeDust)) + power = max(power, c.StrongPower(pos.Side(f), f, w, accountForDust)) } return power } From 6e443d0aec1f17ac767c96a28cd59b88bf0ff62e Mon Sep 17 00:00:00 2001 From: JustTal Date: Fri, 20 Jan 2023 23:40:39 -0600 Subject: [PATCH 34/83] world/tick.go: Undo ordered scheduled block updates + incrementally remove expired updates. --- go.mod | 9 --------- go.sum | 37 ------------------------------------- server/world/conf.go | 3 +-- server/world/tick.go | 16 +++++++++++----- server/world/world.go | 7 +++---- 5 files changed, 15 insertions(+), 57 deletions(-) diff --git a/go.mod b/go.mod index cae6d22b1..989a0eb16 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,6 @@ require ( github.com/df-mc/goleveldb v1.1.9 github.com/go-gl/mathgl v1.0.0 github.com/google/uuid v1.3.0 - github.com/kr/pretty v0.1.0 github.com/pelletier/go-toml v1.9.4 github.com/rogpeppe/go-internal v1.3.0 github.com/sandertv/gophertunnel v1.26.0 @@ -20,19 +19,12 @@ require ( ) require ( - github.com/bahlo/generic-list-go v0.2.0 // indirect - github.com/buger/jsonparser v1.1.1 // indirect - github.com/davecgh/go-spew v1.1.1 // indirect github.com/golang/protobuf v1.5.2 // indirect github.com/golang/snappy v0.0.4 // indirect github.com/klauspost/compress v1.15.1 // indirect - github.com/kr/text v0.1.0 // indirect - github.com/mailru/easyjson v0.7.7 // indirect github.com/muhammadmuzzammil1998/jsonc v1.0.0 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect github.com/sandertv/go-raknet v1.12.0 // indirect github.com/stretchr/testify v1.8.1 // indirect - github.com/wk8/go-ordered-map/v2 v2.1.5 // indirect golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 // indirect golang.org/x/image v0.0.0-20220321031419-a8550c1d254a // indirect golang.org/x/net v0.0.0-20220418201149-a630d4f3e7a2 // indirect @@ -42,5 +34,4 @@ require ( google.golang.org/protobuf v1.28.0 // indirect gopkg.in/square/go-jose.v2 v2.6.0 // indirect gopkg.in/yaml.v2 v2.3.0 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index acebc4827..cc4482d05 100644 --- a/go.sum +++ b/go.sum @@ -35,12 +35,8 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03 github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk= -github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg= github.com/brentp/intintmap v0.0.0-20190211203843-30dc0ade9af9 h1:/G0ghZwrhou0Wq21qc1vXXMm/t/aKWkALWwITptKbE0= github.com/brentp/intintmap v0.0.0-20190211203843-30dc0ade9af9/go.mod h1:TOk10ahXejq9wkEaym3KPRNeuR/h5Jx+s8QRWIa2oTM= -github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs= -github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= @@ -126,20 +122,15 @@ github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= github.com/klauspost/compress v1.15.1 h1:y9FcTHGyrebwfP0ZZqFiaxTaiDnUrGkJkI+f583BL1A= github.com/klauspost/compress v1.15.1/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= -github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= -github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/muhammadmuzzammil1998/jsonc v1.0.0 h1:8o5gBQn4ZA3NBA9DlTujCj2a4w0tqWrPVjDwhzkgTIs= github.com/muhammadmuzzammil1998/jsonc v1.0.0/go.mod h1:saF2fIVw4banK0H4+/EuqfFLpRnoy5S+ECwTOCcRcSU= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= @@ -156,28 +147,6 @@ 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.12.0 h1:olUzZlIJyX/pgj/mrsLCZYjKLNDsYiWdvQ4NIm3z0DA= github.com/sandertv/go-raknet v1.12.0/go.mod h1:Gx+WgZBMQ0V2UoouGoJ8Wj6CDrMBQ4SB2F/ggpl5/+Y= -github.com/sandertv/gophertunnel v1.24.12 h1:V5bLF4edwDBuC7EqyDyhLowo5DiULpLBXp04slnpnFk= -github.com/sandertv/gophertunnel v1.24.12/go.mod h1:dYFetA6r62huhc1EgR9p8VFAFtKOuGgVE/iXf5CzZ4o= -github.com/sandertv/gophertunnel v1.24.14-0.20221019174733-0067b3e28fa6 h1:lJfF2gYL6VH8IiX2CgpKbJtU+jgYDasDHHzzwEZU3Nc= -github.com/sandertv/gophertunnel v1.24.14-0.20221019174733-0067b3e28fa6/go.mod h1:dYFetA6r62huhc1EgR9p8VFAFtKOuGgVE/iXf5CzZ4o= -github.com/sandertv/gophertunnel v1.25.0 h1:XvHDhYtfn2B3TgCl2KqzD0Ifhefb7L7jIH/EUndEBXw= -github.com/sandertv/gophertunnel v1.25.0/go.mod h1:dYFetA6r62huhc1EgR9p8VFAFtKOuGgVE/iXf5CzZ4o= -github.com/sandertv/gophertunnel v1.25.1-0.20221121141016-71f1a0a0b4d1 h1:Y7rhKx90KH2o5gizNPTBaVJ0/uErrcl6z3Sysd9WuYQ= -github.com/sandertv/gophertunnel v1.25.1-0.20221121141016-71f1a0a0b4d1/go.mod h1:dYFetA6r62huhc1EgR9p8VFAFtKOuGgVE/iXf5CzZ4o= -github.com/sandertv/gophertunnel v1.25.1-0.20221128184107-bfbaaa09e996 h1:S9nEW0dnHWbQm3Hp7NoU4dRqXtYE4s4CeDpuX3JWb0A= -github.com/sandertv/gophertunnel v1.25.1-0.20221128184107-bfbaaa09e996/go.mod h1:dYFetA6r62huhc1EgR9p8VFAFtKOuGgVE/iXf5CzZ4o= -github.com/sandertv/gophertunnel v1.25.1-0.20221128185839-cddb45d58786 h1:j8ubuCKLBh3ZgFnWDAA218SFfBmzE8EN1sNxutIHt0I= -github.com/sandertv/gophertunnel v1.25.1-0.20221128185839-cddb45d58786/go.mod h1:dYFetA6r62huhc1EgR9p8VFAFtKOuGgVE/iXf5CzZ4o= -github.com/sandertv/gophertunnel v1.25.1-0.20221128221108-bb208100c82e h1:yOtj0tqpPfeyYx4QZaNGeHm1Ktp0kS2B3nfPidmu39M= -github.com/sandertv/gophertunnel v1.25.1-0.20221128221108-bb208100c82e/go.mod h1:dYFetA6r62huhc1EgR9p8VFAFtKOuGgVE/iXf5CzZ4o= -github.com/sandertv/gophertunnel v1.25.1-0.20221128221659-f7b8a2411c89 h1:inhsYsI+O7BGDjUxeM5bRmjDVEdToROLdA3x8xP1BEI= -github.com/sandertv/gophertunnel v1.25.1-0.20221128221659-f7b8a2411c89/go.mod h1:dYFetA6r62huhc1EgR9p8VFAFtKOuGgVE/iXf5CzZ4o= -github.com/sandertv/gophertunnel v1.25.2 h1:XgBTPW7muLgyHKL9S6NzVWnZYBnH01HVOuwxhNo4RGQ= -github.com/sandertv/gophertunnel v1.25.2/go.mod h1:dYFetA6r62huhc1EgR9p8VFAFtKOuGgVE/iXf5CzZ4o= -github.com/sandertv/gophertunnel v1.25.3-0.20221128223358-89a35151e73e h1:jUSAzchAJREQHrHknMi4nn0+X3+r96KWbKCseLARWRY= -github.com/sandertv/gophertunnel v1.25.3-0.20221128223358-89a35151e73e/go.mod h1:dYFetA6r62huhc1EgR9p8VFAFtKOuGgVE/iXf5CzZ4o= -github.com/sandertv/gophertunnel v1.25.3-0.20221128232255-bce3b31e5608 h1:DuSNpIWNGN1V5yr56lLE1V9L9psoFh5SkwuEqJ61zng= -github.com/sandertv/gophertunnel v1.25.3-0.20221128232255-bce3b31e5608/go.mod h1:dYFetA6r62huhc1EgR9p8VFAFtKOuGgVE/iXf5CzZ4o= github.com/sandertv/gophertunnel v1.26.0 h1:6wCDkCh6j6t8wh0G+3U4X1KNVJtrmlj7yI58LuCh720= github.com/sandertv/gophertunnel v1.26.0/go.mod h1:dYFetA6r62huhc1EgR9p8VFAFtKOuGgVE/iXf5CzZ4o= github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= @@ -190,14 +159,10 @@ github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpE github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/wk8/go-ordered-map/v2 v2.1.5 h1:jLbYIFyWQMUwHLO20cImlCRBoNc5lp0nmE2dvwcxc7k= -github.com/wk8/go-ordered-map/v2 v2.1.5/go.mod h1:9Xvgm2mV2kSq2SAm0Y608tBmu8akTzI7c2bz7/G7ZN4= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -475,8 +440,6 @@ gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/server/world/conf.go b/server/world/conf.go index faadf2294..a6b9ca905 100644 --- a/server/world/conf.go +++ b/server/world/conf.go @@ -4,7 +4,6 @@ import ( "github.com/df-mc/atomic" "github.com/df-mc/dragonfly/server/block/cube" "github.com/sirupsen/logrus" - "github.com/wk8/go-ordered-map/v2" "math/rand" "time" ) @@ -72,7 +71,7 @@ func (conf Config) New() *World { } s := conf.Provider.Settings() w := &World{ - scheduledUpdates: orderedmap.New[cube.Pos, int64](), + scheduledUpdates: make(map[cube.Pos]int64), entities: make(map[Entity]ChunkPos), viewers: make(map[*Loader]Viewer), chunks: make(map[ChunkPos]*chunkData), diff --git a/server/world/tick.go b/server/world/tick.go index 2a09f1f58..cc866d01e 100644 --- a/server/world/tick.go +++ b/server/world/tick.go @@ -77,16 +77,22 @@ func (t ticker) tick() { // tickScheduledBlocks executes scheduled block updates in chunks that are currently loaded. func (t ticker) tickScheduledBlocks(tick int64) { t.w.updateMu.Lock() - positions := make([]cube.Pos, 0, t.w.scheduledUpdates.Len()/4) - for pair := t.w.scheduledUpdates.Oldest(); pair != nil; pair = pair.Next() { - if pair.Value <= tick { - positions = append(positions, pair.Key) - t.w.scheduledUpdates.Delete(pair.Key) + positions := make([]cube.Pos, 0, len(t.w.scheduledUpdates)/4) + for pos, scheduledTick := range t.w.scheduledUpdates { + if scheduledTick <= tick { + positions = append(positions, pos) } } t.w.updateMu.Unlock() for _, pos := range positions { + // We need to incrementally delete each scheduled update from the map, as each block update itself might attempt + // to schedule another block update, which could conflict with the current update selection. + // TODO: Definitely shouldn't lock like this. I need coffee. And sleep. + t.w.updateMu.Lock() + delete(t.w.scheduledUpdates, pos) + t.w.updateMu.Unlock() + if ticker, ok := t.w.Block(pos).(ScheduledTicker); ok { ticker.ScheduledTick(pos, t.w, t.w.r) } diff --git a/server/world/world.go b/server/world/world.go index f8b9679e4..23faa4261 100644 --- a/server/world/world.go +++ b/server/world/world.go @@ -2,7 +2,6 @@ package world import ( "fmt" - orderedmap "github.com/wk8/go-ordered-map/v2" "math/rand" "sync" "time" @@ -60,7 +59,7 @@ type World struct { // scheduledUpdates is a map of tick time values indexed by the block position at which an update is // scheduled. If the current tick exceeds the tick value passed, the block update will be performed // and the entry will be removed from the map. - scheduledUpdates *orderedmap.OrderedMap[cube.Pos, int64] + scheduledUpdates map[cube.Pos]int64 neighbourUpdates []neighbourUpdate viewersMu sync.Mutex @@ -929,14 +928,14 @@ func (w *World) ScheduleBlockUpdate(pos cube.Pos, delay time.Duration) { } w.updateMu.Lock() defer w.updateMu.Unlock() - if _, exists := w.scheduledUpdates.Get(pos); exists { + if _, exists := w.scheduledUpdates[pos]; exists { return } w.set.Lock() t := w.set.CurrentTick w.set.Unlock() - w.scheduledUpdates.Set(pos, t+delay.Nanoseconds()/int64(time.Second/20)) + w.scheduledUpdates[pos] = t + delay.Nanoseconds()/int64(time.Second/20) } // doBlockUpdatesAround schedules block updates directly around and on the position passed. From 7e2a060f73a405744ef8be5d08dc948f04c467d2 Mon Sep 17 00:00:00 2001 From: JustTal Date: Sat, 21 Jan 2023 00:03:30 -0600 Subject: [PATCH 35/83] block/redstone_repeater.go: Fix repeater clocks! --- server/block/redstone_repeater.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/server/block/redstone_repeater.go b/server/block/redstone_repeater.go index e45758612..4ba6044d2 100644 --- a/server/block/redstone_repeater.go +++ b/server/block/redstone_repeater.go @@ -110,13 +110,13 @@ func (r RedstoneRepeater) ScheduledTick(pos cube.Pos, w *world.World, _ *rand.Ra // Ignore this tick; the repeater is locked. return } - if shouldBePowered := r.inputStrength(pos, w) > 0; r.Powered && !shouldBePowered { - r.Powered = false - } else if !r.Powered { - r.Powered = true - if !shouldBePowered { - w.ScheduleBlockUpdate(pos, time.Duration(r.Delay+1)*time.Millisecond*100) - } + + r.Powered = !r.Powered + w.SetBlock(pos, r, nil) + updateGateRedstone(pos, w, r.Facing.Face().Opposite()) + + if r.Powered && r.inputStrength(pos, w) <= 0 { + w.ScheduleBlockUpdate(pos, time.Duration(r.Delay+1)*time.Millisecond*100) } w.SetBlock(pos, r, nil) updateGateRedstone(pos, w, r.Facing.Face()) From 770895c36eebe8d4552225f5ed1ffc3911c3c770 Mon Sep 17 00:00:00 2001 From: DaPigGuy Date: Sat, 21 Jan 2023 00:26:52 -0800 Subject: [PATCH 36/83] block/lever.go: implement basic lever block --- server/block/hash.go | 5 ++ server/block/lever.go | 127 +++++++++++++++++++++++++++++++++++++++ server/block/register.go | 2 + 3 files changed, 134 insertions(+) create mode 100644 server/block/lever.go diff --git a/server/block/hash.go b/server/block/hash.go index 84a9d68d4..352187772 100644 --- a/server/block/hash.go +++ b/server/block/hash.go @@ -92,6 +92,7 @@ const ( hashLapisOre hashLava hashLeaves + hashLever hashLight hashLitPumpkin hashLog @@ -532,6 +533,10 @@ func (l Leaves) Hash() uint64 { return hashLeaves | uint64(l.Wood.Uint8())<<8 | uint64(boolByte(l.Persistent))<<12 | uint64(boolByte(l.ShouldUpdate))<<13 } +func (l Lever) Hash() uint64 { + return hashLever | uint64(boolByte(l.Powered))<<8 | uint64(l.Facing)<<9 | uint64(l.Direction)<<12 +} + func (l Light) Hash() uint64 { return hashLight | uint64(l.Level)<<8 } diff --git a/server/block/lever.go b/server/block/lever.go new file mode 100644 index 000000000..08dd70454 --- /dev/null +++ b/server/block/lever.go @@ -0,0 +1,127 @@ +package block + +import ( + "github.com/df-mc/dragonfly/server/block/cube" + "github.com/df-mc/dragonfly/server/item" + "github.com/df-mc/dragonfly/server/world" + "github.com/go-gl/mathgl/mgl64" +) + +// Lever is a non-solid block that can provide switchable redstone power. +type Lever struct { + empty + flowingWaterDisplacer + + // Powered is if the lever is switched on. + Powered bool + // Facing is the face of the block that the lever is attached to. + Facing cube.Face + // Direction is the direction the lever is pointing. This is only used for levers that are attached on up or down + // faces. + // TODO: Better handle lever direction on up or down faces—using a `cube.Axis` results in a default `Lever` with an + // axis `Y` and a face `Down` which does not map to an existing block state. + Direction cube.Direction +} + +// Source ... +func (l Lever) Source() bool { + return true +} + +// WeakPower ... +func (l Lever) WeakPower(cube.Pos, cube.Face, *world.World, bool) int { + if l.Powered { + return 15 + } + return 0 +} + +// StrongPower ... +func (l Lever) StrongPower(_ cube.Pos, face cube.Face, _ *world.World, _ bool) int { + if l.Powered && l.Facing == face { + return 15 + } + return 0 +} + +// SideClosed ... +func (l Lever) SideClosed(cube.Pos, cube.Pos, *world.World) bool { + return false +} + +// NeighbourUpdateTick ... +func (l Lever) NeighbourUpdateTick(pos, _ cube.Pos, w *world.World) { + if !w.Block(pos.Side(l.Facing.Opposite())).Model().FaceSolid(pos.Side(l.Facing.Opposite()), l.Facing, w) { + w.SetBlock(pos, nil, nil) + dropItem(w, item.NewStack(l, 1), pos.Vec3Centre()) + } +} + +// UseOnBlock ... +func (l Lever) UseOnBlock(pos cube.Pos, face cube.Face, _ mgl64.Vec3, w *world.World, user item.User, ctx *item.UseContext) bool { + pos, face, used := firstReplaceable(w, pos, face, l) + if !used { + return false + } + if !w.Block(pos.Side(face.Opposite())).Model().FaceSolid(pos.Side(face.Opposite()), face, w) { + return false + } + + l.Facing = face + if face == cube.FaceDown || face == cube.FaceUp { + switch user.Rotation().Direction() { + case cube.North, cube.South: + l.Direction = cube.North + case cube.West, cube.East: + l.Direction = cube.West + } + } + place(w, pos, l, user, ctx) + return placed(ctx) +} + +// Activate ... +func (l Lever) Activate(pos cube.Pos, _ cube.Face, w *world.World, _ item.User, _ *item.UseContext) bool { + l.Powered = !l.Powered + w.SetBlock(pos, l, nil) + updateDirectionalRedstone(pos, w, l.Facing.Opposite()) + return true +} + +// BreakInfo ... +func (l Lever) BreakInfo() BreakInfo { + return newBreakInfo(0.5, alwaysHarvestable, nothingEffective, oneOf(l)) +} + +// EncodeItem ... +func (l Lever) EncodeItem() (name string, meta int16) { + return "minecraft:lever", 0 +} + +// EncodeBlock ... +func (l Lever) EncodeBlock() (string, map[string]any) { + direction := l.Facing.String() + if l.Facing == cube.FaceDown || l.Facing == cube.FaceUp { + axis := "east_west" + if l.Direction == cube.North { + axis = "north_south" + } + direction += "_" + axis + } + return "minecraft:lever", map[string]any{"open_bit": l.Powered, "lever_direction": direction} +} + +// allLevers ... +func allLevers() (all []world.Block) { + f := func(facing cube.Face, direction cube.Direction) { + all = append(all, Lever{Facing: facing, Direction: direction}) + all = append(all, Lever{Facing: facing, Direction: direction, Powered: true}) + } + for _, facing := range cube.Faces() { + f(facing, cube.North) + if facing == cube.FaceDown || facing == cube.FaceUp { + f(facing, cube.West) + } + } + return +} diff --git a/server/block/register.go b/server/block/register.go index 1de98ff53..2ecd01dd1 100644 --- a/server/block/register.go +++ b/server/block/register.go @@ -156,6 +156,7 @@ func init() { registerAll(allLanterns()) registerAll(allLava()) registerAll(allLeaves()) + registerAll(allLevers()) registerAll(allLight()) registerAll(allLitPumpkins()) registerAll(allLogs()) @@ -271,6 +272,7 @@ func init() { world.RegisterItem(Kelp{}) world.RegisterItem(Ladder{}) world.RegisterItem(Lapis{}) + world.RegisterItem(Lever{}) world.RegisterItem(LitPumpkin{}) world.RegisterItem(Loom{}) world.RegisterItem(MelonSeeds{}) From b187fead0ecf8e9f258dc2e73b054ce53e24861a Mon Sep 17 00:00:00 2001 From: DaPigGuy Date: Sat, 21 Jan 2023 00:29:03 -0800 Subject: [PATCH 37/83] block/redstone_repeater.go: fixed block placement on delay changing --- server/block/redstone_repeater.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/block/redstone_repeater.go b/server/block/redstone_repeater.go index 4ba6044d2..324acb91c 100644 --- a/server/block/redstone_repeater.go +++ b/server/block/redstone_repeater.go @@ -90,7 +90,7 @@ func (r RedstoneRepeater) Activate(pos cube.Pos, _ cube.Face, w *world.World, _ r.Delay = 0 } w.SetBlock(pos, r, nil) - return false + return true } // RedstoneUpdate ... From 88d2d24da60362341864f862022998d9d7237e6d Mon Sep 17 00:00:00 2001 From: DaPigGuy Date: Sat, 21 Jan 2023 00:31:57 -0800 Subject: [PATCH 38/83] block/tnt.go: ignite on redstone power --- server/block/redstone.go | 9 +++++++++ server/block/redstone_lamp.go | 16 +++------------- server/block/tnt.go | 7 +++++++ 3 files changed, 19 insertions(+), 13 deletions(-) diff --git a/server/block/redstone.go b/server/block/redstone.go index d99d10c43..b9049b80c 100644 --- a/server/block/redstone.go +++ b/server/block/redstone.go @@ -105,6 +105,15 @@ func updateGateRedstone(centre cube.Pos, w *world.World, face cube.Face) { updateAroundRedstone(pos, w, face) } +func receivedRedstonePower(pos cube.Pos, w *world.World) bool { + for _, face := range cube.Faces() { + if w.RedstonePower(pos.Side(face), face, true) > 0 { + return true + } + } + return false +} + // identifyNeighbours identifies the neighbouring positions of a given node, determines their types, and links them into // the graph. After that, based on what nodes in the graph have been visited, the neighbours are reordered left-to-right // relative to the direction of information flow. diff --git a/server/block/redstone_lamp.go b/server/block/redstone_lamp.go index f36668aa9..dcf1c60d8 100644 --- a/server/block/redstone_lamp.go +++ b/server/block/redstone_lamp.go @@ -49,14 +49,14 @@ func (l RedstoneLamp) UseOnBlock(pos cube.Pos, face cube.Face, _ mgl64.Vec3, w * if !used { return } - l.Lit = l.receivedRedstonePower(pos, w) + l.Lit = receivedRedstonePower(pos, w) place(w, pos, l, user, ctx) return placed(ctx) } // RedstoneUpdate ... func (l RedstoneLamp) RedstoneUpdate(pos cube.Pos, w *world.World) { - if l.Lit == l.receivedRedstonePower(pos, w) { + if l.Lit == receivedRedstonePower(pos, w) { return } if !l.Lit { @@ -69,19 +69,9 @@ func (l RedstoneLamp) RedstoneUpdate(pos cube.Pos, w *world.World) { // ScheduledTick ... func (l RedstoneLamp) ScheduledTick(pos cube.Pos, w *world.World, _ *rand.Rand) { - if l.receivedRedstonePower(pos, w) { + if receivedRedstonePower(pos, w) { return } l.Lit = false w.SetBlock(pos, l, &world.SetOpts{DisableBlockUpdates: true}) } - -// receivedRedstonePower ... -func (l RedstoneLamp) receivedRedstonePower(pos cube.Pos, w *world.World) bool { - for _, face := range cube.Faces() { - if w.RedstonePower(pos.Side(face), face, true) > 0 { - return true - } - } - return false -} diff --git a/server/block/tnt.go b/server/block/tnt.go index 32f11ee7d..23cd473c2 100644 --- a/server/block/tnt.go +++ b/server/block/tnt.go @@ -16,6 +16,13 @@ type TNT struct { solid } +// RedstoneUpdate ... +func (t TNT) RedstoneUpdate(pos cube.Pos, w *world.World) { + if receivedRedstonePower(pos, w) { + t.Ignite(pos, w) + } +} + // Activate ... func (t TNT) Activate(pos cube.Pos, _ cube.Face, w *world.World, u item.User, ctx *item.UseContext) bool { held, _ := u.HeldItems() From 3686f0acc6e7d9b8c4db94006f1a7853c4f25070 Mon Sep 17 00:00:00 2001 From: DaPigGuy Date: Sat, 21 Jan 2023 00:37:39 -0800 Subject: [PATCH 39/83] block/tnt.go: ignite on place if redstone powered --- server/block/tnt.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/server/block/tnt.go b/server/block/tnt.go index 23cd473c2..14a1cc660 100644 --- a/server/block/tnt.go +++ b/server/block/tnt.go @@ -16,6 +16,11 @@ type TNT struct { solid } +// NeighbourUpdateTick ... +func (t TNT) NeighbourUpdateTick(pos, _ cube.Pos, w *world.World) { + t.RedstoneUpdate(pos, w) +} + // RedstoneUpdate ... func (t TNT) RedstoneUpdate(pos cube.Pos, w *world.World) { if receivedRedstonePower(pos, w) { From c3eb15cee3893885be178b4a6fd5d696ab02752f Mon Sep 17 00:00:00 2001 From: JustTal Date: Mon, 30 Jan 2023 18:07:20 -0600 Subject: [PATCH 40/83] block: uhhh pistons --- main.go | 64 +--------- server/block/barrel.go | 2 +- server/block/block.go | 14 ++- server/block/cake.go | 5 + server/block/hash.go | 10 ++ server/block/moving.go | 49 ++++++++ server/block/piston.go | 158 +++++++++++++++++++++++++ server/block/piston_arm_collision.go | 49 ++++++++ server/block/redstone.go | 27 ++++- server/block/redstone_comparator.go | 1 + server/block/register.go | 4 + server/internal/sliceutil/sliceutil.go | 19 ++- 12 files changed, 319 insertions(+), 83 deletions(-) create mode 100644 server/block/moving.go create mode 100644 server/block/piston.go create mode 100644 server/block/piston_arm_collision.go diff --git a/main.go b/main.go index 59849e8a8..c21fab758 100644 --- a/main.go +++ b/main.go @@ -3,19 +3,12 @@ package main import ( "fmt" "github.com/df-mc/dragonfly/server" - "github.com/df-mc/dragonfly/server/block" - "github.com/df-mc/dragonfly/server/block/cube" - "github.com/df-mc/dragonfly/server/block/cube/trace" - "github.com/df-mc/dragonfly/server/player" "github.com/df-mc/dragonfly/server/player/chat" - "github.com/df-mc/dragonfly/server/world" - "github.com/go-gl/mathgl/mgl64" "github.com/pelletier/go-toml" "github.com/sirupsen/logrus" "net/http" _ "net/http/pprof" "os" - "time" ) func main() { @@ -41,9 +34,7 @@ func main() { srv.CloseOnProgramEnd() srv.Listen() - for srv.Accept(func(p *player.Player) { - p.Handle(newRedstonePlayerHandler(p)) - }) { + for srv.Accept(nil) { } } @@ -71,56 +62,3 @@ func readConfig(log server.Logger) (server.Config, error) { } return c.Config(log) } - -type redstonePlayerHandler struct { - player.NopHandler - p *player.Player - closeChan chan struct{} -} - -func newRedstonePlayerHandler(p *player.Player) *redstonePlayerHandler { - h := &redstonePlayerHandler{ - p: p, - closeChan: make(chan struct{}, 1), - } - p.ShowCoordinates() - go h.tick() - return h -} - -func (h *redstonePlayerHandler) tick() { - t := time.NewTicker(time.Second / 20) - for { - select { - case <-h.closeChan: - return - case <-t.C: - w := h.p.World() - start := h.p.Position().Add(mgl64.Vec3{0, h.p.EyeHeight()}) - end := start.Add(h.p.Rotation().Vec3().Mul(50)) - var hitBlock world.Block - trace.TraverseBlocks(start, end, func(pos cube.Pos) bool { - b := w.Block(pos) - if _, ok := b.(block.Air); !ok { - hitBlock = b - return false - } - return true - }) - if hitBlock != nil { - popup := fmt.Sprintf("%T", hitBlock) - switch hitBlock := hitBlock.(type) { - case block.RedstoneWire: - popup += fmt.Sprintf("\nPower: %d", hitBlock.Power) - } - h.p.SendPopup(popup) - } else { - h.p.SendPopup("You are not looking at a block") - } - } - } -} - -func (h *redstonePlayerHandler) HandleQuit() { - close(h.closeChan) -} diff --git a/server/block/barrel.go b/server/block/barrel.go index 749f9a1ec..5dc63801c 100644 --- a/server/block/barrel.go +++ b/server/block/barrel.go @@ -116,7 +116,7 @@ func (b Barrel) UseOnBlock(pos cube.Pos, face cube.Face, _ mgl64.Vec3, w *world. } //noinspection GoAssignmentToReceiver b = NewBarrel() - b.Facing = calculateFace(user, pos) + b.Facing = calculateAnySidedFace(user, pos, true) place(w, pos, b, user, ctx) return placed(ctx) diff --git a/server/block/block.go b/server/block/block.go index e3846d14b..865611075 100644 --- a/server/block/block.go +++ b/server/block/block.go @@ -80,12 +80,10 @@ type Frictional interface { Friction() float64 } -var ( - // unknownFace is a face that is used for certain block items. This should not be exposed in the API. - unknownFace = cube.Face(len(cube.Faces())) -) +// unknownFace is a face that is used for certain block items. This should not be exposed in the API. +var unknownFace = cube.Face(len(cube.Faces())) -func calculateFace(user item.User, placePos cube.Pos) cube.Face { +func calculateAnySidedFace(user item.User, placePos cube.Pos, swapHorizontal bool) cube.Face { userPos := user.Position() pos := cube.PosFromVec3(userPos) if abs(pos[0]-placePos[0]) < 2 && abs(pos[2]-placePos[2]) < 2 { @@ -100,7 +98,11 @@ func calculateFace(user item.User, placePos cube.Pos) cube.Face { return cube.FaceDown } } - return user.Rotation().Direction().Opposite().Face() + face := user.Rotation().Direction().Face() + if swapHorizontal { + face = face.Opposite() + } + return face } func abs(x int) int { diff --git a/server/block/cake.go b/server/block/cake.go index c1e880744..0bedd23da 100644 --- a/server/block/cake.go +++ b/server/block/cake.go @@ -24,6 +24,11 @@ func (c Cake) SideClosed(cube.Pos, cube.Pos, *world.World) bool { return false } +// ComparatorSignal ... +func (c Cake) ComparatorSignal(cube.Pos, *world.World) int { + return (7 - c.Bites) * 2 +} + // UseOnBlock ... func (c Cake) UseOnBlock(pos cube.Pos, face cube.Face, _ mgl64.Vec3, w *world.World, user item.User, ctx *item.UseContext) bool { pos, _, used := firstReplaceable(w, pos, face, c) diff --git a/server/block/hash.go b/server/block/hash.go index 352187772..556ab6b3f 100644 --- a/server/block/hash.go +++ b/server/block/hash.go @@ -100,6 +100,7 @@ const ( hashMelon hashMelonSeeds hashMossCarpet + hashMoving hashMud hashMudBricks hashMuddyMangroveRoots @@ -116,6 +117,7 @@ const ( hashObsidian hashPackedIce hashPackedMud + hashPiston hashPlanks hashPodzol hashPolishedBlackstoneBrick @@ -565,6 +567,10 @@ func (MossCarpet) Hash() uint64 { return hashMossCarpet } +func (Moving) Hash() uint64 { + return hashMoving +} + func (Mud) Hash() uint64 { return hashMud } @@ -629,6 +635,10 @@ func (PackedMud) Hash() uint64 { return hashPackedMud } +func (p Piston) Hash() uint64 { + return hashPiston | uint64(p.Facing)<<8 | uint64(boolByte(p.Sticky))<<11 +} + func (p Planks) Hash() uint64 { return hashPlanks | uint64(p.Wood.Uint8())<<8 } diff --git a/server/block/moving.go b/server/block/moving.go new file mode 100644 index 000000000..c5499bdc4 --- /dev/null +++ b/server/block/moving.go @@ -0,0 +1,49 @@ +package block + +import ( + "github.com/df-mc/dragonfly/server/block/cube" + "github.com/df-mc/dragonfly/server/internal/nbtconv" + "github.com/df-mc/dragonfly/server/world" +) + +// Moving ... +type Moving struct { + empty + transparent + + // Moving represents the block that is moving. + Moving world.Block + // Extra represents an extra block that is moving with the main block. + Extra world.Block + // Piston is the position of the piston that is moving the block. + Piston cube.Pos +} + +// EncodeBlock ... +func (Moving) EncodeBlock() (string, map[string]any) { + return "minecraft:moving_block", nil +} + +// EncodeNBT ... +func (b Moving) EncodeNBT() map[string]any { + return map[string]any{ + "id": "Moving", + "movingBlock": nbtconv.WriteBlock(b.Moving), + "movingBlockExtra": nbtconv.WriteBlock(b.Extra), + "pistonPosX": int32(b.Piston.X()), + "pistonPosY": int32(b.Piston.Y()), + "pistonPosZ": int32(b.Piston.Z()), + } +} + +// DecodeNBT ... +func (b Moving) DecodeNBT(m map[string]any) any { + b.Moving = nbtconv.Block(m, "movingBlock") + b.Extra = nbtconv.Block(m, "movingBlockExtra") + b.Piston = cube.Pos{ + int(nbtconv.Int32(m, "pistonPosX")), + int(nbtconv.Int32(m, "pistonPosY")), + int(nbtconv.Int32(m, "pistonPosZ")), + } + return b +} diff --git a/server/block/piston.go b/server/block/piston.go new file mode 100644 index 000000000..3d58530cc --- /dev/null +++ b/server/block/piston.go @@ -0,0 +1,158 @@ +package block + +import ( + "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/world" + "github.com/go-gl/mathgl/mgl64" + "math/rand" + "time" +) + +// Piston is a block capable of pushing blocks, players, and mobs when given a redstone pulse. +type Piston struct { + solid + + // Facing represents the direction the piston is facing. + Facing cube.Face + // Sticky is true if the piston is sticky, false if not. + Sticky bool + + // AttachedBlocks ... + // TODO: Make this a []cube.Pos and convert to []int32 on encode. + AttachedBlocks []int32 + // BreakBlocks ... + // TODO: Make this a []cube.Pos and convert to []int32 on encode. + BreakBlocks []int32 + + // Progress is how far the block has been moved. It can either be 0.0, 0.5, or 1.0. + Progress float64 + // LastProgress ... + LastProgress float64 + + // State ... + State int + // NewState ... + NewState int +} + +// BreakInfo ... +func (p Piston) BreakInfo() BreakInfo { + return newBreakInfo(1.5, alwaysHarvestable, pickaxeEffective, oneOf(p)) +} + +// EncodeItem ... +func (p Piston) EncodeItem() (name string, meta int16) { + if p.Sticky { + return "minecraft:sticky_piston", 0 + } + return "minecraft:piston", 0 +} + +// EncodeBlock ... +func (p Piston) EncodeBlock() (string, map[string]any) { + if p.Sticky { + return "minecraft:sticky_piston", map[string]any{"facing_direction": int32(p.Facing)} + } + return "minecraft:piston", map[string]any{"facing_direction": int32(p.Facing)} +} + +// UseOnBlock ... +func (p Piston) UseOnBlock(pos cube.Pos, face cube.Face, _ mgl64.Vec3, w *world.World, user item.User, ctx *item.UseContext) bool { + pos, _, used := firstReplaceable(w, pos, face, p) + if !used { + return false + } + p.Facing = calculateAnySidedFace(user, pos, false) + + place(w, pos, p, user, ctx) + return placed(ctx) +} + +// EncodeNBT ... +func (p Piston) EncodeNBT() map[string]any { + return map[string]any{ + "AttachedBlocks": p.AttachedBlocks, + "BreakBlocks": p.BreakBlocks, + + "LastProgress": float32(p.LastProgress), + "Progress": float32(p.Progress), + + "NewState": uint8(p.NewState), + "State": uint8(p.State), + + "Sticky": boolByte(p.Sticky), + + "id": "PistonArm", + } +} + +// DecodeNBT ... +func (p Piston) DecodeNBT(m map[string]any) any { + if attached := nbtconv.Slice(m, "AttachedBlocks"); attached != nil { + p.AttachedBlocks = make([]int32, len(attached)) + for i, v := range attached { + p.AttachedBlocks[i] = v.(int32) + } + } + if breakBlocks := nbtconv.Slice(m, "BreakBlocks"); breakBlocks != nil { + p.BreakBlocks = make([]int32, len(breakBlocks)) + for i, v := range breakBlocks { + p.BreakBlocks[i] = v.(int32) + } + } + p.LastProgress = float64(nbtconv.Float32(m, "LastProgress")) + p.Progress = float64(nbtconv.Float32(m, "Progress")) + p.NewState = int(nbtconv.Uint8(m, "NewState")) + p.State = int(nbtconv.Uint8(m, "State")) + p.Sticky = nbtconv.Bool(m, "Sticky") + return p +} + +// RedstoneUpdate ... +func (Piston) RedstoneUpdate(pos cube.Pos, w *world.World) { + w.ScheduleBlockUpdate(pos, time.Millisecond*50) +} + +// ScheduledTick ... +func (p Piston) ScheduledTick(pos cube.Pos, w *world.World, _ *rand.Rand) { + if receivedRedstonePower(pos, w, p.armFace()) { + if !p.push(pos, w) { + return + } + } else if !p.pull(pos, w) { + return + } + w.ScheduleBlockUpdate(pos, time.Millisecond*50) +} + +// armFace ... +func (p Piston) armFace() cube.Face { + if p.Facing.Axis() == cube.Y { + return p.Facing + } + return p.Facing.Opposite() +} + +// push ... +func (p Piston) push(pos cube.Pos, w *world.World) bool { + //TODO: Implement. + return false +} + +// pull ... +func (p Piston) pull(pos cube.Pos, w *world.World) bool { + //TODO: Implement. + return false +} + +// allPistons ... +func allPistons() (pistons []world.Block) { + for _, f := range cube.Faces() { + for _, s := range []bool{false, true} { + pistons = append(pistons, Piston{Facing: f, Sticky: s}) + } + } + return +} diff --git a/server/block/piston_arm_collision.go b/server/block/piston_arm_collision.go new file mode 100644 index 000000000..68b6d35a4 --- /dev/null +++ b/server/block/piston_arm_collision.go @@ -0,0 +1,49 @@ +package block + +import ( + "github.com/df-mc/dragonfly/server/block/cube" + "github.com/df-mc/dragonfly/server/item" + "github.com/df-mc/dragonfly/server/world" +) + +// PistonArmCollision is a block that is used when a piston is extended and colliding with a block. +type PistonArmCollision struct { + empty + transparent + + // Facing represents the direction the piston is facing. + Facing cube.Face + // Sticky is true if the piston arm is sticky. + Sticky bool +} + +// BreakInfo ... +func (c PistonArmCollision) BreakInfo() BreakInfo { + return newBreakInfo(1.5, alwaysHarvestable, pickaxeEffective, simpleDrops()).withBreakHandler(func(pos cube.Pos, w *world.World, u item.User) { + pistonPos := pos.Side(c.pistonFace()) + if p, ok := w.Block(pistonPos).(Piston); ok { + w.SetBlock(pistonPos, nil, nil) + dropItem(w, item.NewStack(p, 1), pos.Vec3Centre()) + } + }) +} + +// EncodeBlock ... +func (c PistonArmCollision) EncodeBlock() (string, map[string]any) { + return "minecraft:piston_arm_collision", map[string]any{"facing_direction": int32(c.Facing)} +} + +// NeighbourUpdateTick ... +func (c PistonArmCollision) NeighbourUpdateTick(pos, _ cube.Pos, w *world.World) { + if _, ok := w.Block(pos.Side(c.pistonFace())).(Piston); !ok { + w.SetBlock(pos, nil, nil) + } +} + +// pistonFace ... +func (c PistonArmCollision) pistonFace() cube.Face { + if c.Facing.Axis() != cube.Y { + return c.Facing + } + return c.Facing.Opposite() +} diff --git a/server/block/redstone.go b/server/block/redstone.go index b9049b80c..b7d013f07 100644 --- a/server/block/redstone.go +++ b/server/block/redstone.go @@ -3,6 +3,7 @@ package block import ( "github.com/df-mc/dragonfly/server/block/cube" "github.com/df-mc/dragonfly/server/block/model" + "github.com/df-mc/dragonfly/server/item/inventory" "github.com/df-mc/dragonfly/server/world" "golang.org/x/exp/slices" ) @@ -13,6 +14,27 @@ type RedstoneUpdater interface { RedstoneUpdate(pos cube.Pos, w *world.World) } +// ComparatorEmitter represents a block that can emit a redstone signal through a comparator. +type ComparatorEmitter interface { + // ComparatorSignal returns the signal strength that is emitted through a comparator. + ComparatorSignal(pos cube.Pos, w *world.World) int +} + +// inventoryComparatorOutput calculates the output of a comparator based on the contents of an inventory. +func inventoryComparatorOutput(inv *inventory.Inventory) int { + if inv.Empty() { + return 0 + } + var amount int + for _, st := range inv.Slots() { + if st.Empty() { + continue + } + amount += st.Count() / st.MaxCount() + } + return (amount/inv.Size())*14 + 1 +} + // wireNetwork implements a minimally-invasive bolt-on accelerator that performs a breadth-first search through redstone // wires in order to more efficiently and compute new redstone wire power levels and determine the order in which other // blocks should be updated. This implementation is heavily based off of RedstoneWireTurbo and MCHPRS. @@ -105,8 +127,11 @@ func updateGateRedstone(centre cube.Pos, w *world.World, face cube.Face) { updateAroundRedstone(pos, w, face) } -func receivedRedstonePower(pos cube.Pos, w *world.World) bool { +func receivedRedstonePower(pos cube.Pos, w *world.World, ignoredFaces ...cube.Face) bool { for _, face := range cube.Faces() { + if slices.Contains(ignoredFaces, face) { + continue + } if w.RedstonePower(pos.Side(face), face, true) > 0 { return true } diff --git a/server/block/redstone_comparator.go b/server/block/redstone_comparator.go index d3bc37589..3a8e1275a 100644 --- a/server/block/redstone_comparator.go +++ b/server/block/redstone_comparator.go @@ -11,6 +11,7 @@ import ( // RedstoneComparator is a redstone component used to maintain, compare, or subtract signal strength, or to measure // certain block states (primarily the fullness of containers). +// TODO: Implement functionality. type RedstoneComparator struct { transparent diff --git a/server/block/register.go b/server/block/register.go index 2ecd01dd1..470ddfe2c 100644 --- a/server/block/register.go +++ b/server/block/register.go @@ -62,6 +62,7 @@ func init() { world.RegisterBlock(Lapis{}) world.RegisterBlock(Melon{}) world.RegisterBlock(MossCarpet{}) + world.RegisterBlock(Moving{}) world.RegisterBlock(MudBricks{}) world.RegisterBlock(Mud{}) world.RegisterBlock(NetherBrickFence{}) @@ -165,6 +166,7 @@ func init() { registerAll(allMuddyMangroveRoots()) registerAll(allNetherBricks()) registerAll(allNetherWart()) + registerAll(allPistons()) registerAll(allPlanks()) registerAll(allPotato()) registerAll(allPrismarine()) @@ -295,6 +297,8 @@ func init() { world.RegisterItem(Obsidian{}) world.RegisterItem(PackedIce{}) world.RegisterItem(PackedMud{}) + world.RegisterItem(Piston{}) + world.RegisterItem(Piston{Sticky: true}) world.RegisterItem(Podzol{}) world.RegisterItem(PolishedBlackstoneBrick{Cracked: true}) world.RegisterItem(PolishedBlackstoneBrick{}) diff --git a/server/internal/sliceutil/sliceutil.go b/server/internal/sliceutil/sliceutil.go index 739ec8d71..0e5957f51 100644 --- a/server/internal/sliceutil/sliceutil.go +++ b/server/internal/sliceutil/sliceutil.go @@ -2,8 +2,7 @@ package sliceutil import "golang.org/x/exp/slices" -// Convert converts a slice of type B to a slice of type A. Convert panics if B -// cannot be type asserted to type A. +// Convert converts a slice of type B to a slice of type A. Convert panics if B cannot be type asserted to type A. func Convert[A, B any, S ~[]B](v S) []A { a := make([]A, len(v)) for i, b := range v { @@ -12,9 +11,8 @@ func Convert[A, B any, S ~[]B](v S) []A { return a } -// Index returns the index of the first occurrence of v in s, or -1 if not -// present. Index accepts any type, as opposed to slices.Index, but might panic -// if E is not comparable. +// Index returns the index of the first occurrence of v in s, or -1 if not present. Index accepts any type, as opposed +// to slices.Index, but might panic if E is not comparable. func Index[E any](s []E, v E) int { for i, vs := range s { if (any)(v) == (any)(vs) { @@ -24,9 +22,8 @@ func Index[E any](s []E, v E) int { return -1 } -// SearchValue iterates through slice v, calling function f for every element. -// If true is returned in this function, the respective element is returned and -// ok is true. If the function f does not return true for any element, false is +// SearchValue iterates through slice v, calling function f for every element. If true is returned in this function, the +// respective element is returned and ok is true. If the function f does not return true for any element, false is // returned. func SearchValue[A any, S ~[]A](v S, f func(a A) bool) (a A, ok bool) { for _, val := range v { @@ -37,8 +34,7 @@ func SearchValue[A any, S ~[]A](v S, f func(a A) bool) (a A, ok bool) { return } -// Filter iterates over elements of collection, returning an array of all -// elements function c returns true for. +// Filter iterates over elements of collection, returning an array of all elements function c returns true for. func Filter[E any](s []E, c func(E) bool) []E { a := make([]E, 0, len(s)) for _, e := range s { @@ -49,8 +45,7 @@ func Filter[E any](s []E, c func(E) bool) []E { return a } -// DeleteVal deletes the first occurrence of a value in a slice of the type E -// and returns a new slice without the value. +// DeleteVal deletes the first occurrence of a value in a slice of the type E and returns a new slice without the value. func DeleteVal[E any](s []E, v E) []E { if i := Index(s, v); i != -1 { return slices.Clone(slices.Delete(s, i, i+1)) From 08797bae45dd0d97ae8920b088b5b968e7364ed4 Mon Sep 17 00:00:00 2001 From: JustTal Date: Tue, 31 Jan 2023 19:38:46 -0600 Subject: [PATCH 41/83] block/piston_resolver.go: piston resolvers kill me --- server/block/banner.go | 5 + server/block/barrier.go | 5 + server/block/beacon.go | 5 + server/block/bedrock.go | 5 + server/block/cube/face.go | 9 ++ server/block/dragon_egg.go | 5 + server/block/enchanting_table.go | 5 + server/block/ender_chest.go | 5 + server/block/invisible_bedrock.go | 5 + server/block/ladder.go | 5 + server/block/lava.go | 5 + server/block/leaves.go | 5 + server/block/lit_pumpkin.go | 5 + server/block/melon.go | 5 + server/block/moving.go | 5 + server/block/obsidian.go | 8 ++ server/block/piston_arm_collision.go | 15 ++- server/block/piston_resolver.go | 141 +++++++++++++++++++++++++++ server/block/pumpkin.go | 5 + server/block/redstone.go | 12 +++ server/block/sign.go | 5 + server/block/water.go | 5 + server/block/wood_door.go | 5 + 23 files changed, 270 insertions(+), 5 deletions(-) create mode 100644 server/block/piston_resolver.go diff --git a/server/block/banner.go b/server/block/banner.go index ad265fa2c..6414ef4cc 100644 --- a/server/block/banner.go +++ b/server/block/banner.go @@ -30,6 +30,11 @@ func (Banner) MaxCount() int { return 16 } +// PistonBreakable ... +func (Banner) PistonBreakable() bool { + return true +} + // BreakInfo ... func (b Banner) BreakInfo() BreakInfo { return newBreakInfo(1, alwaysHarvestable, axeEffective, oneOf(b)) diff --git a/server/block/barrier.go b/server/block/barrier.go index 2d645e02f..056c9dd32 100644 --- a/server/block/barrier.go +++ b/server/block/barrier.go @@ -17,6 +17,11 @@ func (Barrier) SideClosed(cube.Pos, cube.Pos, *world.World) bool { return false } +// PistonImmovable ... +func (Barrier) PistonImmovable() bool { + return true +} + // EncodeItem ... func (Barrier) EncodeItem() (name string, meta int16) { return "minecraft:barrier", 0 diff --git a/server/block/beacon.go b/server/block/beacon.go index e391d5e53..d671d3ea0 100644 --- a/server/block/beacon.go +++ b/server/block/beacon.go @@ -84,6 +84,11 @@ func (Beacon) LightEmissionLevel() uint8 { return 15 } +// PistonImmovable ... +func (Beacon) PistonImmovable() bool { + return true +} + // Level returns an integer 0-4 which defines the current pyramid level of the beacon. func (b Beacon) Level() int { return b.level diff --git a/server/block/bedrock.go b/server/block/bedrock.go index bb10292c9..4525c5286 100644 --- a/server/block/bedrock.go +++ b/server/block/bedrock.go @@ -16,6 +16,11 @@ func (Bedrock) EncodeItem() (name string, meta int16) { return "minecraft:bedrock", 0 } +// PistonImmovable ... +func (Bedrock) PistonImmovable() bool { + return true +} + // EncodeBlock ... func (b Bedrock) EncodeBlock() (name string, properties map[string]any) { //noinspection SpellCheckingInspection diff --git a/server/block/cube/face.go b/server/block/cube/face.go index 12049e597..b707b9bcb 100644 --- a/server/block/cube/face.go +++ b/server/block/cube/face.go @@ -24,6 +24,15 @@ func (f Face) Direction() Direction { return Direction(f - 2) } +// Positive returns whether the face is the positive of its axis. For example, FaceEast is positive, FaceWest is not. +func (f Face) Positive() bool { + switch f { + case FaceUp, FaceSouth, FaceEast: + return true + } + return false +} + // Opposite returns the opposite face. FaceDown will return up, north will return south and west will return east, // and vice versa. func (f Face) Opposite() Face { diff --git a/server/block/dragon_egg.go b/server/block/dragon_egg.go index f2b446b1f..afa53ed87 100644 --- a/server/block/dragon_egg.go +++ b/server/block/dragon_egg.go @@ -27,6 +27,11 @@ func (d DragonEgg) SideClosed(cube.Pos, cube.Pos, *world.World) bool { return false } +// PistonBreakable ... +func (DragonEgg) PistonBreakable() bool { + return true +} + // teleport ... func (d DragonEgg) teleport(pos cube.Pos, w *world.World) { for i := 0; i < 1000; i++ { diff --git a/server/block/enchanting_table.go b/server/block/enchanting_table.go index 86c7f01a9..85a24f59c 100644 --- a/server/block/enchanting_table.go +++ b/server/block/enchanting_table.go @@ -35,6 +35,11 @@ func (EnchantingTable) LightEmissionLevel() uint8 { return 7 } +// PistonImmovable ... +func (EnchantingTable) PistonImmovable() bool { + return true +} + // Activate ... func (EnchantingTable) Activate(pos cube.Pos, _ cube.Face, _ *world.World, u item.User, _ *item.UseContext) bool { if opener, ok := u.(ContainerOpener); ok { diff --git a/server/block/ender_chest.go b/server/block/ender_chest.go index 85f991680..96e81b6bc 100644 --- a/server/block/ender_chest.go +++ b/server/block/ender_chest.go @@ -45,6 +45,11 @@ func (c EnderChest) LightEmissionLevel() uint8 { return 7 } +// PistonImmovable ... +func (EnderChest) PistonImmovable() bool { + return true +} + // SideClosed ... func (EnderChest) SideClosed(cube.Pos, cube.Pos, *world.World) bool { return false diff --git a/server/block/invisible_bedrock.go b/server/block/invisible_bedrock.go index 54a6aeceb..6556e00f9 100644 --- a/server/block/invisible_bedrock.go +++ b/server/block/invisible_bedrock.go @@ -7,6 +7,11 @@ type InvisibleBedrock struct { solid } +// PistonImmovable ... +func (InvisibleBedrock) PistonImmovable() bool { + return true +} + // EncodeItem ... func (InvisibleBedrock) EncodeItem() (name string, meta int16) { return "minecraft:invisible_bedrock", 0 diff --git a/server/block/ladder.go b/server/block/ladder.go index 0e62a68ca..413eb0d7a 100644 --- a/server/block/ladder.go +++ b/server/block/ladder.go @@ -69,6 +69,11 @@ func (l Ladder) SideClosed(cube.Pos, cube.Pos, *world.World) bool { return false } +// PistonBreakable ... +func (Ladder) PistonBreakable() bool { + return true +} + // BreakInfo ... func (l Ladder) BreakInfo() BreakInfo { return newBreakInfo(0.4, alwaysHarvestable, axeEffective, oneOf(l)) diff --git a/server/block/lava.go b/server/block/lava.go index 2cfd934a8..1610519c6 100644 --- a/server/block/lava.go +++ b/server/block/lava.go @@ -87,6 +87,11 @@ func (Lava) LightEmissionLevel() uint8 { return 15 } +// PistonBreakable ... +func (Lava) PistonBreakable() bool { + return true +} + // NeighbourUpdateTick ... func (l Lava) NeighbourUpdateTick(pos, _ cube.Pos, w *world.World) { if !l.Harden(pos, w, nil) { diff --git a/server/block/leaves.go b/server/block/leaves.go index 3f5af6357..3dfca135b 100644 --- a/server/block/leaves.go +++ b/server/block/leaves.go @@ -123,6 +123,11 @@ func (Leaves) LightDiffusionLevel() uint8 { return 1 } +// PistonBreakable ... +func (Leaves) PistonBreakable() bool { + return true +} + // SideClosed ... func (Leaves) SideClosed(cube.Pos, cube.Pos, *world.World) bool { return false diff --git a/server/block/lit_pumpkin.go b/server/block/lit_pumpkin.go index d2d746987..5f6e91a08 100644 --- a/server/block/lit_pumpkin.go +++ b/server/block/lit_pumpkin.go @@ -20,6 +20,11 @@ func (l LitPumpkin) LightEmissionLevel() uint8 { return 15 } +// PistonBreakable ... +func (LitPumpkin) PistonBreakable() bool { + return true +} + // UseOnBlock ... func (l LitPumpkin) 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) diff --git a/server/block/melon.go b/server/block/melon.go index 93f338148..d79f2e662 100644 --- a/server/block/melon.go +++ b/server/block/melon.go @@ -15,6 +15,11 @@ func (m Melon) BreakInfo() BreakInfo { return newBreakInfo(1, alwaysHarvestable, axeEffective, silkTouchDrop(item.NewStack(item.MelonSlice{}, rand.Intn(5)+3), item.NewStack(m, 1))) } +// PistonBreakable ... +func (Melon) PistonBreakable() bool { + return true +} + // CompostChance ... func (Melon) CompostChance() float64 { return 0.65 diff --git a/server/block/moving.go b/server/block/moving.go index c5499bdc4..8365373f8 100644 --- a/server/block/moving.go +++ b/server/block/moving.go @@ -24,6 +24,11 @@ func (Moving) EncodeBlock() (string, map[string]any) { return "minecraft:moving_block", nil } +// PistonImmovable ... +func (Moving) PistonImmovable() bool { + return true +} + // EncodeNBT ... func (b Moving) EncodeNBT() map[string]any { return map[string]any{ diff --git a/server/block/obsidian.go b/server/block/obsidian.go index 5d99bd4a0..af3f082aa 100644 --- a/server/block/obsidian.go +++ b/server/block/obsidian.go @@ -13,6 +13,14 @@ type Obsidian struct { Crying bool } +// PistonImmovable ... +func (o Obsidian) PistonImmovable() bool { + if o.Crying { + return false + } + return true +} + // LightEmissionLevel ... func (o Obsidian) LightEmissionLevel() uint8 { if o.Crying { diff --git a/server/block/piston_arm_collision.go b/server/block/piston_arm_collision.go index 68b6d35a4..b4469e47d 100644 --- a/server/block/piston_arm_collision.go +++ b/server/block/piston_arm_collision.go @@ -17,6 +17,16 @@ type PistonArmCollision struct { Sticky bool } +// PistonImmovable ... +func (PistonArmCollision) PistonImmovable() bool { + return true +} + +// EncodeBlock ... +func (c PistonArmCollision) EncodeBlock() (string, map[string]any) { + return "minecraft:piston_arm_collision", map[string]any{"facing_direction": int32(c.Facing)} +} + // BreakInfo ... func (c PistonArmCollision) BreakInfo() BreakInfo { return newBreakInfo(1.5, alwaysHarvestable, pickaxeEffective, simpleDrops()).withBreakHandler(func(pos cube.Pos, w *world.World, u item.User) { @@ -28,11 +38,6 @@ func (c PistonArmCollision) BreakInfo() BreakInfo { }) } -// EncodeBlock ... -func (c PistonArmCollision) EncodeBlock() (string, map[string]any) { - return "minecraft:piston_arm_collision", map[string]any{"facing_direction": int32(c.Facing)} -} - // NeighbourUpdateTick ... func (c PistonArmCollision) NeighbourUpdateTick(pos, _ cube.Pos, w *world.World) { if _, ok := w.Block(pos.Side(c.pistonFace())).(Piston); !ok { diff --git a/server/block/piston_resolver.go b/server/block/piston_resolver.go new file mode 100644 index 000000000..6f09cbfc4 --- /dev/null +++ b/server/block/piston_resolver.go @@ -0,0 +1,141 @@ +package block + +import ( + "github.com/df-mc/dragonfly/server/block/cube" + "github.com/df-mc/dragonfly/server/world" + "sort" +) + +// pistonResolver ... +type pistonResolver struct { + w *world.World + pos cube.Pos + + sticky bool + push bool + + attachedBlocks []cube.Pos + breakBlocks []cube.Pos + + checked map[cube.Pos]struct{} + + success bool +} + +// newPistonResolver ... +func newPistonResolver(w *world.World, pos cube.Pos, sticky, push bool) *pistonResolver { + return &pistonResolver{ + w: w, + pos: pos, + + sticky: sticky, + push: push, + + checked: make(map[cube.Pos]struct{}), + } +} + +// resolve ... +func (r *pistonResolver) resolve() { + piston, ok := r.w.Block(r.pos).(Piston) + if !ok { + return + } + face := piston.armFace() + if r.push { + if r.calculateBlocks(r.pos.Side(face), face, face) { + r.success = true + } + } else { + if r.sticky { + r.calculateBlocks(r.pos.Side(face).Side(face), face, face.Opposite()) + } + r.success = true + } + sort.SliceStable(r.attachedBlocks, func(i, j int) bool { + posOne := r.attachedBlocks[i] + posTwo := r.attachedBlocks[j] + + push := 1 + if !r.push { + push = -1 + } + + positive := 1 + if !face.Positive() { + positive = -1 + } + + direction := push * positive + switch face.Axis() { + case cube.Y: + return (posTwo.Y()-posOne.Y())*direction > 0 + case cube.Z: + return (posTwo.Z()-posOne.Z())*direction > 0 + case cube.X: + return (posTwo.X()-posOne.X())*direction > 0 + } + panic("should never happen") + }) +} + +// calculateBlocks ... +func (r *pistonResolver) calculateBlocks(pos cube.Pos, face cube.Face, breakFace cube.Face) bool { + if _, ok := r.checked[pos]; ok { + return true + } + r.checked[pos] = struct{}{} + + block := r.w.Block(pos) + if _, ok := block.(Air); ok { + return true + } + if !r.canMove(pos, block) { + if face == breakFace { + r.breakBlocks = nil + r.attachedBlocks = nil + return false + } + return true + } + if r.canBreak(block) { + if face == breakFace { + r.breakBlocks = append(r.breakBlocks, pos) + } + return true + } + if pos.Side(breakFace).OutOfBounds(r.w.Range()) { + r.breakBlocks = nil + r.attachedBlocks = nil + return false + } + + r.attachedBlocks = append(r.attachedBlocks, pos) + if len(r.attachedBlocks) >= 13 { + r.breakBlocks = nil + r.attachedBlocks = nil + return false + } + return r.calculateBlocks(pos.Side(breakFace), breakFace, breakFace) +} + +// canMove ... +func (r *pistonResolver) canMove(pos cube.Pos, block world.Block) bool { + if p, ok := block.(Piston); ok { + if r.pos == pos { + return false + } + return p.State == 0 + } + p, ok := block.(PistonImmovable) + return !ok || !p.PistonImmovable() +} + +// canBreak ... +func (r *pistonResolver) canBreak(block world.Block) bool { + if l, ok := block.(LiquidRemovable); ok && l.HasLiquidDrops() { + return true + } + p, ok := block.(PistonBreakable) + return ok && p.PistonBreakable() +} diff --git a/server/block/pumpkin.go b/server/block/pumpkin.go index 87a1c7c06..02f0d7f53 100644 --- a/server/block/pumpkin.go +++ b/server/block/pumpkin.go @@ -73,6 +73,11 @@ func (p Pumpkin) KnockBackResistance() float64 { return 0 } +// PistonBreakable ... +func (Pumpkin) PistonBreakable() bool { + return true +} + // EncodeItem ... func (p Pumpkin) EncodeItem() (name string, meta int16) { if p.Carved { diff --git a/server/block/redstone.go b/server/block/redstone.go index b7d013f07..d4451bf5f 100644 --- a/server/block/redstone.go +++ b/server/block/redstone.go @@ -20,6 +20,18 @@ type ComparatorEmitter interface { ComparatorSignal(pos cube.Pos, w *world.World) int } +// PistonImmovable represents a block that cannot be moved by a piston. +type PistonImmovable interface { + // PistonImmovable returns whether the block is immovable. + PistonImmovable() bool +} + +// PistonBreakable represents a block that can be broken by a piston. +type PistonBreakable interface { + // PistonBreakable returns whether the block can be broken by a piston. + PistonBreakable() bool +} + // inventoryComparatorOutput calculates the output of a comparator based on the contents of an inventory. func inventoryComparatorOutput(inv *inventory.Inventory) int { if inv.Empty() { diff --git a/server/block/sign.go b/server/block/sign.go index b1102e6dd..e8ec276fa 100644 --- a/server/block/sign.go +++ b/server/block/sign.go @@ -47,6 +47,11 @@ func (s Sign) MaxCount() int { return 16 } +// PistonBreakable ... +func (Sign) PistonBreakable() bool { + return true +} + // FlammabilityInfo ... func (s Sign) FlammabilityInfo() FlammabilityInfo { return newFlammabilityInfo(0, 0, true) diff --git a/server/block/water.go b/server/block/water.go index ff27ae4bd..478f835b9 100644 --- a/server/block/water.go +++ b/server/block/water.go @@ -54,6 +54,11 @@ func (Water) SpreadDecay() int { return 1 } +// PistonBreakable ... +func (Water) PistonBreakable() bool { + return true +} + // WithDepth returns the water with the depth passed. func (w Water) WithDepth(depth int, falling bool) world.Liquid { w.Depth = depth diff --git a/server/block/wood_door.go b/server/block/wood_door.go index c88ff4835..c056499d7 100644 --- a/server/block/wood_door.go +++ b/server/block/wood_door.go @@ -48,6 +48,11 @@ func (d WoodDoor) Model() world.BlockModel { return model.Door{Facing: d.Facing, Open: d.Open, Right: d.Right} } +// PistonBreakable ... +func (WoodDoor) PistonBreakable() bool { + return true +} + // NeighbourUpdateTick ... func (d WoodDoor) NeighbourUpdateTick(pos, _ cube.Pos, w *world.World) { if d.Top { From 42dde290be1504df7432c17ac158924c9648c64a Mon Sep 17 00:00:00 2001 From: JustTal Date: Tue, 31 Jan 2023 20:41:02 -0600 Subject: [PATCH 42/83] block/piston.go: pushing somewhat --- main.go | 5 +- server/block/hash.go | 5 ++ server/block/moving.go | 15 +++++- server/block/obsidian.go | 5 +- server/block/piston.go | 76 ++++++++++++++++++++++++++-- server/block/piston_arm_collision.go | 16 ++++-- server/block/piston_resolver.go | 75 ++++++++++++--------------- server/block/register.go | 1 + 8 files changed, 143 insertions(+), 55 deletions(-) diff --git a/main.go b/main.go index c21fab758..2ea89718f 100644 --- a/main.go +++ b/main.go @@ -3,6 +3,7 @@ package main import ( "fmt" "github.com/df-mc/dragonfly/server" + "github.com/df-mc/dragonfly/server/player" "github.com/df-mc/dragonfly/server/player/chat" "github.com/pelletier/go-toml" "github.com/sirupsen/logrus" @@ -34,7 +35,9 @@ func main() { srv.CloseOnProgramEnd() srv.Listen() - for srv.Accept(nil) { + for srv.Accept(func(p *player.Player) { + p.ShowCoordinates() + }) { } } diff --git a/server/block/hash.go b/server/block/hash.go index 556ab6b3f..0ec05df01 100644 --- a/server/block/hash.go +++ b/server/block/hash.go @@ -118,6 +118,7 @@ const ( hashPackedIce hashPackedMud hashPiston + hashPistonArmCollision hashPlanks hashPodzol hashPolishedBlackstoneBrick @@ -639,6 +640,10 @@ func (p Piston) Hash() uint64 { return hashPiston | uint64(p.Facing)<<8 | uint64(boolByte(p.Sticky))<<11 } +func (c PistonArmCollision) Hash() uint64 { + return hashPistonArmCollision | uint64(c.Facing)<<8 +} + func (p Planks) Hash() uint64 { return hashPlanks | uint64(p.Wood.Uint8())<<8 } diff --git a/server/block/moving.go b/server/block/moving.go index 8365373f8..e9e120121 100644 --- a/server/block/moving.go +++ b/server/block/moving.go @@ -31,7 +31,13 @@ func (Moving) PistonImmovable() bool { // EncodeNBT ... func (b Moving) EncodeNBT() map[string]any { - return map[string]any{ + if b.Moving == nil { + b.Moving = Air{} + } + if b.Extra == nil { + b.Extra = Air{} + } + data := map[string]any{ "id": "Moving", "movingBlock": nbtconv.WriteBlock(b.Moving), "movingBlockExtra": nbtconv.WriteBlock(b.Extra), @@ -39,6 +45,10 @@ func (b Moving) EncodeNBT() map[string]any { "pistonPosY": int32(b.Piston.Y()), "pistonPosZ": int32(b.Piston.Z()), } + if nbt, ok := b.Moving.(world.NBTer); ok { + data["movingEntity"] = nbt.EncodeNBT() + } + return data } // DecodeNBT ... @@ -50,5 +60,8 @@ func (b Moving) DecodeNBT(m map[string]any) any { int(nbtconv.Int32(m, "pistonPosY")), int(nbtconv.Int32(m, "pistonPosZ")), } + if nbt, ok := b.Moving.(world.NBTer); ok { + b.Moving = nbt.DecodeNBT(m["movingEntity"].(map[string]any)).(world.Block) + } return b } diff --git a/server/block/obsidian.go b/server/block/obsidian.go index af3f082aa..0e68d7b81 100644 --- a/server/block/obsidian.go +++ b/server/block/obsidian.go @@ -15,10 +15,7 @@ type Obsidian struct { // PistonImmovable ... func (o Obsidian) PistonImmovable() bool { - if o.Crying { - return false - } - return true + return !o.Crying } // LightEmissionLevel ... diff --git a/server/block/piston.go b/server/block/piston.go index 3d58530cc..c0980a788 100644 --- a/server/block/piston.go +++ b/server/block/piston.go @@ -39,7 +39,7 @@ type Piston struct { // BreakInfo ... func (p Piston) BreakInfo() BreakInfo { - return newBreakInfo(1.5, alwaysHarvestable, pickaxeEffective, oneOf(p)) + return newBreakInfo(1.5, alwaysHarvestable, pickaxeEffective, oneOf(Piston{Sticky: p.Sticky})) } // EncodeItem ... @@ -137,8 +137,78 @@ func (p Piston) armFace() cube.Face { // push ... func (p Piston) push(pos cube.Pos, w *world.World) bool { - //TODO: Implement. - return false + if p.State == 0 { + resolver := pistonResolve(w, pos, p, true) + if !resolver.success { + return false + } + + for _, breakPos := range resolver.breakPositions { + p.BreakBlocks = append(p.BreakBlocks, int32(breakPos.X()), int32(breakPos.Y()), int32(breakPos.Z())) + if b, ok := w.Block(breakPos).(Breakable); ok { + w.SetBlock(breakPos, nil, nil) + for _, drop := range b.BreakInfo().Drops(item.ToolNone{}, nil) { + dropItem(w, drop, breakPos.Vec3Centre()) + } + } + } + + face := p.armFace() + for _, attachedPos := range resolver.attachedPositions { + side := attachedPos.Side(face) + p.AttachedBlocks = append(p.AttachedBlocks, int32(side.X()), int32(side.Y()), int32(side.Z())) + + w.SetBlock(side, Moving{Piston: pos, Moving: w.Block(attachedPos)}, nil) + w.SetBlock(attachedPos, nil, nil) + updateAroundRedstone(attachedPos, w) + } + + p.State = 1 + w.SetBlock(pos.Side(face), PistonArmCollision{Facing: p.Facing}, nil) + } else if p.State == 1 { + if p.Progress == 1 { + p.State = 2 + } + p.LastProgress = p.Progress + + if p.State == 1 { + p.Progress += 0.5 + if p.Progress == 0.5 { + // TODO: Sound! + } + } + + if p.State == 2 { + for i := 0; i < len(p.AttachedBlocks); i += 3 { + x := p.AttachedBlocks[i] + y := p.AttachedBlocks[i+1] + z := p.AttachedBlocks[i+2] + + attachPos := cube.Pos{int(x), int(y), int(z)} + moving, ok := w.Block(attachPos).(Moving) + if !ok { + continue + } + w.SetBlock(attachPos, moving.Moving, nil) + if r, ok := moving.Moving.(RedstoneUpdater); ok { + r.RedstoneUpdate(attachPos, w) + } + updateAroundRedstone(attachPos, w) + } + + p.AttachedBlocks = nil + p.BreakBlocks = nil + } + return false + } else if p.State == 3 { + return p.pull(pos, w) + } else { + return false + } + + p.NewState = p.State + w.SetBlock(pos, p, nil) + return true } // pull ... diff --git a/server/block/piston_arm_collision.go b/server/block/piston_arm_collision.go index b4469e47d..6125a4963 100644 --- a/server/block/piston_arm_collision.go +++ b/server/block/piston_arm_collision.go @@ -13,8 +13,6 @@ type PistonArmCollision struct { // Facing represents the direction the piston is facing. Facing cube.Face - // Sticky is true if the piston arm is sticky. - Sticky bool } // PistonImmovable ... @@ -33,7 +31,11 @@ func (c PistonArmCollision) BreakInfo() BreakInfo { pistonPos := pos.Side(c.pistonFace()) if p, ok := w.Block(pistonPos).(Piston); ok { w.SetBlock(pistonPos, nil, nil) - dropItem(w, item.NewStack(p, 1), pos.Vec3Centre()) + if g, ok := u.(interface { + GameMode() world.GameMode + }); !ok || !g.GameMode().CreativeInventory() { + dropItem(w, item.NewStack(Piston{Sticky: p.Sticky}, 1), pos.Vec3Centre()) + } } }) } @@ -52,3 +54,11 @@ func (c PistonArmCollision) pistonFace() cube.Face { } return c.Facing.Opposite() } + +// allPistonArmCollisions ... +func allPistonArmCollisions() (pistonArmCollisions []world.Block) { + for _, f := range cube.Faces() { + pistonArmCollisions = append(pistonArmCollisions, PistonArmCollision{Facing: f}) + } + return +} diff --git a/server/block/piston_resolver.go b/server/block/piston_resolver.go index 6f09cbfc4..89e5d57b6 100644 --- a/server/block/piston_resolver.go +++ b/server/block/piston_resolver.go @@ -11,54 +11,41 @@ type pistonResolver struct { w *world.World pos cube.Pos - sticky bool - push bool + attachedPositions []cube.Pos + breakPositions []cube.Pos - attachedBlocks []cube.Pos - breakBlocks []cube.Pos - - checked map[cube.Pos]struct{} + history map[cube.Pos]struct{} success bool } -// newPistonResolver ... -func newPistonResolver(w *world.World, pos cube.Pos, sticky, push bool) *pistonResolver { - return &pistonResolver{ +// pistonResolve ... +func pistonResolve(w *world.World, pos cube.Pos, piston Piston, push bool) *pistonResolver { + r := &pistonResolver{ w: w, pos: pos, - sticky: sticky, - push: push, - - checked: make(map[cube.Pos]struct{}), + history: make(map[cube.Pos]struct{}), } -} -// resolve ... -func (r *pistonResolver) resolve() { - piston, ok := r.w.Block(r.pos).(Piston) - if !ok { - return - } face := piston.armFace() - if r.push { + if push { if r.calculateBlocks(r.pos.Side(face), face, face) { r.success = true } } else { - if r.sticky { + if piston.Sticky { r.calculateBlocks(r.pos.Side(face).Side(face), face, face.Opposite()) } r.success = true } - sort.SliceStable(r.attachedBlocks, func(i, j int) bool { - posOne := r.attachedBlocks[i] - posTwo := r.attachedBlocks[j] + sort.SliceStable(r.attachedPositions, func(i, j int) bool { + posOne := r.attachedPositions[i] + posTwo := r.attachedPositions[j] - push := 1 - if !r.push { - push = -1 + pushI := 1 + if !push { + pushI = -1 } positive := 1 @@ -66,25 +53,27 @@ func (r *pistonResolver) resolve() { positive = -1 } - direction := push * positive + offset := posTwo.Sub(posOne) + direction := pushI * positive switch face.Axis() { case cube.Y: - return (posTwo.Y()-posOne.Y())*direction > 0 + return offset.Y()*direction > 0 case cube.Z: - return (posTwo.Z()-posOne.Z())*direction > 0 + return offset.Z()*direction > 0 case cube.X: - return (posTwo.X()-posOne.X())*direction > 0 + return offset.X()*direction > 0 } panic("should never happen") }) + return r } // calculateBlocks ... func (r *pistonResolver) calculateBlocks(pos cube.Pos, face cube.Face, breakFace cube.Face) bool { - if _, ok := r.checked[pos]; ok { + if _, ok := r.history[pos]; ok { return true } - r.checked[pos] = struct{}{} + r.history[pos] = struct{}{} block := r.w.Block(pos) if _, ok := block.(Air); ok { @@ -92,28 +81,28 @@ func (r *pistonResolver) calculateBlocks(pos cube.Pos, face cube.Face, breakFace } if !r.canMove(pos, block) { if face == breakFace { - r.breakBlocks = nil - r.attachedBlocks = nil + r.breakPositions = nil + r.attachedPositions = nil return false } return true } if r.canBreak(block) { if face == breakFace { - r.breakBlocks = append(r.breakBlocks, pos) + r.breakPositions = append(r.breakPositions, pos) } return true } if pos.Side(breakFace).OutOfBounds(r.w.Range()) { - r.breakBlocks = nil - r.attachedBlocks = nil + r.breakPositions = nil + r.attachedPositions = nil return false } - r.attachedBlocks = append(r.attachedBlocks, pos) - if len(r.attachedBlocks) >= 13 { - r.breakBlocks = nil - r.attachedBlocks = nil + r.attachedPositions = append(r.attachedPositions, pos) + if len(r.attachedPositions) >= 13 { + r.breakPositions = nil + r.attachedPositions = nil return false } return r.calculateBlocks(pos.Side(breakFace), breakFace, breakFace) diff --git a/server/block/register.go b/server/block/register.go index 470ddfe2c..8f3ea0f95 100644 --- a/server/block/register.go +++ b/server/block/register.go @@ -166,6 +166,7 @@ func init() { registerAll(allMuddyMangroveRoots()) registerAll(allNetherBricks()) registerAll(allNetherWart()) + registerAll(allPistonArmCollisions()) registerAll(allPistons()) registerAll(allPlanks()) registerAll(allPotato()) From d6c3189ba4a68d3fff90e9c54df2fc60e4863aee Mon Sep 17 00:00:00 2001 From: JustTal Date: Tue, 31 Jan 2023 21:09:48 -0600 Subject: [PATCH 43/83] block/piston.go: pulling but it's still really broken lmao --- server/block/piston.go | 132 ++++++++++++++++++++++++++++++++--------- 1 file changed, 104 insertions(+), 28 deletions(-) diff --git a/server/block/piston.go b/server/block/piston.go index c0980a788..84fc9336c 100644 --- a/server/block/piston.go +++ b/server/block/piston.go @@ -20,11 +20,9 @@ type Piston struct { Sticky bool // AttachedBlocks ... - // TODO: Make this a []cube.Pos and convert to []int32 on encode. - AttachedBlocks []int32 + AttachedBlocks []cube.Pos // BreakBlocks ... - // TODO: Make this a []cube.Pos and convert to []int32 on encode. - BreakBlocks []int32 + BreakBlocks []cube.Pos // Progress is how far the block has been moved. It can either be 0.0, 0.5, or 1.0. Progress float64 @@ -72,9 +70,17 @@ func (p Piston) UseOnBlock(pos cube.Pos, face cube.Face, _ mgl64.Vec3, w *world. // EncodeNBT ... func (p Piston) EncodeNBT() map[string]any { + attachedBlocks := make([]int32, 0, len(p.AttachedBlocks)*3) + for _, pos := range p.AttachedBlocks { + attachedBlocks = append(attachedBlocks, int32(pos[0]), int32(pos[1]), int32(pos[2])) + } + breakBlocks := make([]int32, 0, len(p.BreakBlocks)*3) + for _, pos := range p.BreakBlocks { + breakBlocks = append(breakBlocks, int32(pos[0]), int32(pos[1]), int32(pos[2])) + } return map[string]any{ - "AttachedBlocks": p.AttachedBlocks, - "BreakBlocks": p.BreakBlocks, + "AttachedBlocks": attachedBlocks, + "BreakBlocks": breakBlocks, "LastProgress": float32(p.LastProgress), "Progress": float32(p.Progress), @@ -91,15 +97,23 @@ func (p Piston) EncodeNBT() map[string]any { // DecodeNBT ... func (p Piston) DecodeNBT(m map[string]any) any { if attached := nbtconv.Slice(m, "AttachedBlocks"); attached != nil { - p.AttachedBlocks = make([]int32, len(attached)) - for i, v := range attached { - p.AttachedBlocks[i] = v.(int32) + p.AttachedBlocks = make([]cube.Pos, 0, len(attached)/3) + for i := 0; i < len(attached); i += 3 { + p.AttachedBlocks = append(p.AttachedBlocks, cube.Pos{ + int(attached[i].(int32)), + int(attached[i+1].(int32)), + int(attached[i+2].(int32)), + }) } } if breakBlocks := nbtconv.Slice(m, "BreakBlocks"); breakBlocks != nil { - p.BreakBlocks = make([]int32, len(breakBlocks)) - for i, v := range breakBlocks { - p.BreakBlocks[i] = v.(int32) + p.BreakBlocks = make([]cube.Pos, 0, len(breakBlocks)/3) + for i := 0; i < len(breakBlocks); i += 3 { + p.BreakBlocks = append(p.BreakBlocks, cube.Pos{ + int(breakBlocks[i].(int32)), + int(breakBlocks[i+1].(int32)), + int(breakBlocks[i+2].(int32)), + }) } } p.LastProgress = float64(nbtconv.Float32(m, "LastProgress")) @@ -144,7 +158,7 @@ func (p Piston) push(pos cube.Pos, w *world.World) bool { } for _, breakPos := range resolver.breakPositions { - p.BreakBlocks = append(p.BreakBlocks, int32(breakPos.X()), int32(breakPos.Y()), int32(breakPos.Z())) + p.BreakBlocks = append(p.BreakBlocks, breakPos) if b, ok := w.Block(breakPos).(Breakable); ok { w.SetBlock(breakPos, nil, nil) for _, drop := range b.BreakInfo().Drops(item.ToolNone{}, nil) { @@ -156,7 +170,7 @@ func (p Piston) push(pos cube.Pos, w *world.World) bool { face := p.armFace() for _, attachedPos := range resolver.attachedPositions { side := attachedPos.Side(face) - p.AttachedBlocks = append(p.AttachedBlocks, int32(side.X()), int32(side.Y()), int32(side.Z())) + p.AttachedBlocks = append(p.AttachedBlocks, side) w.SetBlock(side, Moving{Piston: pos, Moving: w.Block(attachedPos)}, nil) w.SetBlock(attachedPos, nil, nil) @@ -174,32 +188,27 @@ func (p Piston) push(pos cube.Pos, w *world.World) bool { if p.State == 1 { p.Progress += 0.5 if p.Progress == 0.5 { - // TODO: Sound! + // TODO: Out sound! } } if p.State == 2 { - for i := 0; i < len(p.AttachedBlocks); i += 3 { - x := p.AttachedBlocks[i] - y := p.AttachedBlocks[i+1] - z := p.AttachedBlocks[i+2] - - attachPos := cube.Pos{int(x), int(y), int(z)} - moving, ok := w.Block(attachPos).(Moving) + for _, attachedPos := range p.AttachedBlocks { + moving, ok := w.Block(attachedPos).(Moving) if !ok { continue } - w.SetBlock(attachPos, moving.Moving, nil) + + w.SetBlock(attachedPos, moving.Moving, nil) if r, ok := moving.Moving.(RedstoneUpdater); ok { - r.RedstoneUpdate(attachPos, w) + r.RedstoneUpdate(attachedPos, w) } - updateAroundRedstone(attachPos, w) + updateAroundRedstone(attachedPos, w) } p.AttachedBlocks = nil p.BreakBlocks = nil } - return false } else if p.State == 3 { return p.pull(pos, w) } else { @@ -213,8 +222,75 @@ func (p Piston) push(pos cube.Pos, w *world.World) bool { // pull ... func (p Piston) pull(pos cube.Pos, w *world.World) bool { - //TODO: Implement. - return false + if p.State == 2 { + face := p.armFace() + w.SetBlock(pos.Side(face), nil, nil) + + resolver := pistonResolve(w, pos, p, false) + if !resolver.success { + return false + } + + for _, breakPos := range resolver.breakPositions { + p.BreakBlocks = append(p.BreakBlocks, breakPos) + if b, ok := w.Block(breakPos).(Breakable); ok { + w.SetBlock(breakPos, nil, nil) + for _, drop := range b.BreakInfo().Drops(item.ToolNone{}, nil) { + dropItem(w, drop, breakPos.Vec3Centre()) + } + } + } + + face = face.Opposite() + for _, attachedPos := range resolver.attachedPositions { + side := attachedPos.Side(face) + p.AttachedBlocks = append(p.AttachedBlocks, side) + + w.SetBlock(side, Moving{Piston: pos, Moving: w.Block(attachedPos)}, nil) + w.SetBlock(attachedPos, nil, nil) + updateAroundRedstone(attachedPos, w) + } + + p.State = 3 + } else if p.State == 3 { + if p.Progress == 0 { + p.State = 0 + } + p.LastProgress = p.Progress + + if p.State == 3 { + p.Progress -= 0.5 + if p.Progress == 0.5 { + // TODO: In sound! + } + } + + if p.State == 0 { + for _, attachedPos := range p.AttachedBlocks { + moving, ok := w.Block(attachedPos).(Moving) + if !ok { + continue + } + + w.SetBlock(attachedPos, moving.Moving, nil) + if r, ok := moving.Moving.(RedstoneUpdater); ok { + r.RedstoneUpdate(attachedPos, w) + } + updateAroundRedstone(attachedPos, w) + } + + p.AttachedBlocks = nil + p.BreakBlocks = nil + } + } else if p.State == 1 { + return p.push(pos, w) + } else { + return false + } + + p.NewState = p.State + w.SetBlock(pos, p, nil) + return true } // allPistons ... From 6721636eccf826491cf7096c42ce23aa9ffa860e Mon Sep 17 00:00:00 2001 From: DaPigGuy Date: Tue, 31 Jan 2023 19:43:10 -0800 Subject: [PATCH 44/83] block/lever.go: fixes --- server/block/lever.go | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/server/block/lever.go b/server/block/lever.go index 08dd70454..0ff1f4149 100644 --- a/server/block/lever.go +++ b/server/block/lever.go @@ -68,13 +68,9 @@ func (l Lever) UseOnBlock(pos cube.Pos, face cube.Face, _ mgl64.Vec3, w *world.W } l.Facing = face - if face == cube.FaceDown || face == cube.FaceUp { - switch user.Rotation().Direction() { - case cube.North, cube.South: - l.Direction = cube.North - case cube.West, cube.East: - l.Direction = cube.West - } + l.Direction = cube.North + if face.Axis() == cube.Y && user.Rotation().Direction().Face().Axis() == cube.X { + l.Direction = cube.West } place(w, pos, l, user, ctx) return placed(ctx) @@ -90,7 +86,9 @@ func (l Lever) Activate(pos cube.Pos, _ cube.Face, w *world.World, _ item.User, // BreakInfo ... func (l Lever) BreakInfo() BreakInfo { - return newBreakInfo(0.5, alwaysHarvestable, nothingEffective, oneOf(l)) + return newBreakInfo(0.5, alwaysHarvestable, nothingEffective, oneOf(l)).withBreakHandler(func(pos cube.Pos, w *world.World, _ item.User) { + updateDirectionalRedstone(pos, w, l.Facing.Opposite()) + }) } // EncodeItem ... From 58c14806617b5f72de3a9a508a752edbb6c6d22d Mon Sep 17 00:00:00 2001 From: JustTal Date: Tue, 31 Jan 2023 22:17:14 -0600 Subject: [PATCH 45/83] block/piston.go: reee --- server/block/moving.go | 8 +++++++- server/block/piston.go | 4 ++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/server/block/moving.go b/server/block/moving.go index e9e120121..28944a6b5 100644 --- a/server/block/moving.go +++ b/server/block/moving.go @@ -1,6 +1,7 @@ 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/world" @@ -17,6 +18,8 @@ type Moving struct { Extra world.Block // Piston is the position of the piston that is moving the block. Piston cube.Pos + // Expanding is true if the moving block is expanding, false if it is contracting. + Expanding bool } // EncodeBlock ... @@ -38,7 +41,8 @@ func (b Moving) EncodeNBT() map[string]any { b.Extra = Air{} } data := map[string]any{ - "id": "Moving", + "id": "MovingBlock", + "expanding": b.Expanding, "movingBlock": nbtconv.WriteBlock(b.Moving), "movingBlockExtra": nbtconv.WriteBlock(b.Extra), "pistonPosX": int32(b.Piston.X()), @@ -48,11 +52,13 @@ func (b Moving) EncodeNBT() map[string]any { if nbt, ok := b.Moving.(world.NBTer); ok { data["movingEntity"] = nbt.EncodeNBT() } + fmt.Println(data) return data } // DecodeNBT ... func (b Moving) DecodeNBT(m map[string]any) any { + b.Expanding = nbtconv.Bool(m, "expanding") b.Moving = nbtconv.Block(m, "movingBlock") b.Extra = nbtconv.Block(m, "movingBlockExtra") b.Piston = cube.Pos{ diff --git a/server/block/piston.go b/server/block/piston.go index 84fc9336c..02ec1035e 100644 --- a/server/block/piston.go +++ b/server/block/piston.go @@ -88,7 +88,7 @@ func (p Piston) EncodeNBT() map[string]any { "NewState": uint8(p.NewState), "State": uint8(p.State), - "Sticky": boolByte(p.Sticky), + "Sticky": p.Sticky, "id": "PistonArm", } @@ -172,7 +172,7 @@ func (p Piston) push(pos cube.Pos, w *world.World) bool { side := attachedPos.Side(face) p.AttachedBlocks = append(p.AttachedBlocks, side) - w.SetBlock(side, Moving{Piston: pos, Moving: w.Block(attachedPos)}, nil) + w.SetBlock(side, Moving{Piston: pos, Moving: w.Block(attachedPos), Expanding: true}, nil) w.SetBlock(attachedPos, nil, nil) updateAroundRedstone(attachedPos, w) } From c555828c4ff8e3b4a332b2c5597c9be7642963a4 Mon Sep 17 00:00:00 2001 From: JustTal Date: Wed, 1 Feb 2023 13:03:23 -0600 Subject: [PATCH 46/83] block/piston.go: Various fixes to pushing and pulling. --- server/block/moving.go | 2 -- server/block/piston.go | 25 +++++++++++++++---------- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/server/block/moving.go b/server/block/moving.go index 28944a6b5..2efb8e40b 100644 --- a/server/block/moving.go +++ b/server/block/moving.go @@ -1,7 +1,6 @@ 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/world" @@ -52,7 +51,6 @@ func (b Moving) EncodeNBT() map[string]any { if nbt, ok := b.Moving.(world.NBTer); ok { data["movingEntity"] = nbt.EncodeNBT() } - fmt.Println(data) return data } diff --git a/server/block/piston.go b/server/block/piston.go index 02ec1035e..3c924d3f7 100644 --- a/server/block/piston.go +++ b/server/block/piston.go @@ -13,6 +13,7 @@ import ( // Piston is a block capable of pushing blocks, players, and mobs when given a redstone pulse. type Piston struct { solid + transparent // Facing represents the direction the piston is facing. Facing cube.Face @@ -170,7 +171,7 @@ func (p Piston) push(pos cube.Pos, w *world.World) bool { face := p.armFace() for _, attachedPos := range resolver.attachedPositions { side := attachedPos.Side(face) - p.AttachedBlocks = append(p.AttachedBlocks, side) + p.AttachedBlocks = append(p.AttachedBlocks, attachedPos) w.SetBlock(side, Moving{Piston: pos, Moving: w.Block(attachedPos), Expanding: true}, nil) w.SetBlock(attachedPos, nil, nil) @@ -193,17 +194,19 @@ func (p Piston) push(pos cube.Pos, w *world.World) bool { } if p.State == 2 { + face := p.armFace() for _, attachedPos := range p.AttachedBlocks { - moving, ok := w.Block(attachedPos).(Moving) + side := attachedPos.Side(face) + moving, ok := w.Block(side).(Moving) if !ok { continue } - w.SetBlock(attachedPos, moving.Moving, nil) + w.SetBlock(side, moving.Moving, nil) if r, ok := moving.Moving.(RedstoneUpdater); ok { - r.RedstoneUpdate(attachedPos, w) + r.RedstoneUpdate(side, w) } - updateAroundRedstone(attachedPos, w) + updateAroundRedstone(side, w) } p.AttachedBlocks = nil @@ -244,7 +247,7 @@ func (p Piston) pull(pos cube.Pos, w *world.World) bool { face = face.Opposite() for _, attachedPos := range resolver.attachedPositions { side := attachedPos.Side(face) - p.AttachedBlocks = append(p.AttachedBlocks, side) + p.AttachedBlocks = append(p.AttachedBlocks, attachedPos) w.SetBlock(side, Moving{Piston: pos, Moving: w.Block(attachedPos)}, nil) w.SetBlock(attachedPos, nil, nil) @@ -266,17 +269,19 @@ func (p Piston) pull(pos cube.Pos, w *world.World) bool { } if p.State == 0 { + face := p.armFace() for _, attachedPos := range p.AttachedBlocks { - moving, ok := w.Block(attachedPos).(Moving) + side := attachedPos.Side(face.Opposite()) + moving, ok := w.Block(side).(Moving) if !ok { continue } - w.SetBlock(attachedPos, moving.Moving, nil) + w.SetBlock(side, moving.Moving, nil) if r, ok := moving.Moving.(RedstoneUpdater); ok { - r.RedstoneUpdate(attachedPos, w) + r.RedstoneUpdate(side, w) } - updateAroundRedstone(attachedPos, w) + updateAroundRedstone(side, w) } p.AttachedBlocks = nil From 61a40ba1f186a04a7cd778214c93303fb836f3dc Mon Sep 17 00:00:00 2001 From: JustTal Date: Wed, 1 Feb 2023 13:35:30 -0600 Subject: [PATCH 47/83] block/piston_resolver.go: fix sorting! --- server/block/piston_resolver.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/block/piston_resolver.go b/server/block/piston_resolver.go index 89e5d57b6..704f1e443 100644 --- a/server/block/piston_resolver.go +++ b/server/block/piston_resolver.go @@ -53,7 +53,7 @@ func pistonResolve(w *world.World, pos cube.Pos, piston Piston, push bool) *pist positive = -1 } - offset := posTwo.Sub(posOne) + offset := posOne.Sub(posTwo) direction := pushI * positive switch face.Axis() { case cube.Y: From f4f0f971b56ef0e620b30176afd8f318adf41f75 Mon Sep 17 00:00:00 2001 From: JustTal Date: Wed, 1 Feb 2023 19:29:46 -0600 Subject: [PATCH 48/83] block: Update neighbouring redstone on dependent break. --- server/block/lever.go | 2 ++ server/block/redstone_torch.go | 1 + 2 files changed, 3 insertions(+) diff --git a/server/block/lever.go b/server/block/lever.go index 0ff1f4149..e6f9f1479 100644 --- a/server/block/lever.go +++ b/server/block/lever.go @@ -10,6 +10,7 @@ import ( // Lever is a non-solid block that can provide switchable redstone power. type Lever struct { empty + transparent flowingWaterDisplacer // Powered is if the lever is switched on. @@ -54,6 +55,7 @@ func (l Lever) NeighbourUpdateTick(pos, _ cube.Pos, w *world.World) { if !w.Block(pos.Side(l.Facing.Opposite())).Model().FaceSolid(pos.Side(l.Facing.Opposite()), l.Facing, w) { w.SetBlock(pos, nil, nil) dropItem(w, item.NewStack(l, 1), pos.Vec3Centre()) + updateDirectionalRedstone(pos, w, l.Facing.Opposite()) } } diff --git a/server/block/redstone_torch.go b/server/block/redstone_torch.go index 305b50c6a..c36a97387 100644 --- a/server/block/redstone_torch.go +++ b/server/block/redstone_torch.go @@ -83,6 +83,7 @@ func (t RedstoneTorch) NeighbourUpdateTick(pos, _ cube.Pos, w *world.World) { if !w.Block(pos.Side(t.Facing)).Model().FaceSolid(pos.Side(t.Facing), t.Facing.Opposite(), w) { w.SetBlock(pos, nil, nil) dropItem(w, item.NewStack(t, 1), pos.Vec3Centre()) + updateDirectionalRedstone(pos, w, t.Facing.Opposite()) } } From 791dc227d9bf0d326c8603866b319937c1318b6e Mon Sep 17 00:00:00 2001 From: JustTal Date: Wed, 1 Feb 2023 19:30:02 -0600 Subject: [PATCH 49/83] world/world.go: Account for transparent blocks. --- server/world/world.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/server/world/world.go b/server/world/world.go index 23faa4261..f3c5d0b91 100644 --- a/server/world/world.go +++ b/server/world/world.go @@ -1009,6 +1009,9 @@ func (w *World) RedstonePower(pos cube.Pos, face cube.Face, accountForDust bool) if c, ok := b.(Conductor); ok { power = c.WeakPower(pos, face, w, accountForDust) } + if d, ok := b.(lightDiffuser); ok && d.LightDiffusionLevel() == 0 { + return power + } for _, f := range cube.Faces() { if !b.Model().FaceSolid(pos, f, w) { return power From 7a7cd2471b868dea82ed669836d45fe9882cfdfc Mon Sep 17 00:00:00 2001 From: JustTal Date: Wed, 1 Feb 2023 19:44:19 -0600 Subject: [PATCH 50/83] block/redstone_wire.go: Various fixes! --- server/block/redstone_repeater.go | 8 +------- server/block/redstone_wire.go | 7 ++++++- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/server/block/redstone_repeater.go b/server/block/redstone_repeater.go index 324acb91c..860467aa4 100644 --- a/server/block/redstone_repeater.go +++ b/server/block/redstone_repeater.go @@ -149,13 +149,7 @@ func (r RedstoneRepeater) StrongPower(pos cube.Pos, face cube.Face, w *world.Wor // inputStrength ... func (r RedstoneRepeater) inputStrength(pos cube.Pos, w *world.World) int { face := r.Facing.Face() - sidePos := pos.Side(face) - - strength := w.RedstonePower(sidePos, face, true) - if w, ok := w.Block(sidePos).(RedstoneWire); ok { - return max(strength, w.Power) - } - return strength + return w.RedstonePower(pos.Side(face), face, true) } // allRedstoneRepeaters ... diff --git a/server/block/redstone_wire.go b/server/block/redstone_wire.go index 13c2405a3..02a606639 100644 --- a/server/block/redstone_wire.go +++ b/server/block/redstone_wire.go @@ -96,10 +96,13 @@ func (r RedstoneWire) WeakPower(pos cube.Pos, face cube.Face, w *world.World, ac if !accountForDust { return 0 } + if face == cube.FaceUp { + return r.Power + } if face == cube.FaceDown { return 0 } - if face == cube.FaceUp { + if r.connection(pos, face.Opposite(), w) { return r.Power } if r.connection(pos, face, w) && !r.connection(pos, face.RotateLeft(), w) && !r.connection(pos, face.RotateRight(), w) { @@ -167,6 +170,8 @@ func (RedstoneWire) connectsTo(block world.Block, face cube.Face, allowDirectSou return true case RedstoneRepeater: return r.Facing.Face() == face || r.Facing.Face().Opposite() == face + case Piston: + return true } // TODO: Account for observers. c, ok := block.(world.Conductor) From b29f602aeb8812324ec886b245742cc6ebd39a49 Mon Sep 17 00:00:00 2001 From: JustTal Date: Wed, 1 Feb 2023 20:00:18 -0600 Subject: [PATCH 51/83] block/piston.go: Trigger redstone update on placement. --- server/block/piston.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/server/block/piston.go b/server/block/piston.go index 3c924d3f7..91f5589ae 100644 --- a/server/block/piston.go +++ b/server/block/piston.go @@ -66,7 +66,11 @@ func (p Piston) UseOnBlock(pos cube.Pos, face cube.Face, _ mgl64.Vec3, w *world. p.Facing = calculateAnySidedFace(user, pos, false) place(w, pos, p, user, ctx) - return placed(ctx) + if placed(ctx) { + p.RedstoneUpdate(pos, w) + return true + } + return false } // EncodeNBT ... From 6ce58b4ee864a11b0585096b2469932fe9e5aa76 Mon Sep 17 00:00:00 2001 From: JustTal Date: Wed, 1 Feb 2023 20:03:24 -0600 Subject: [PATCH 52/83] block/piston.go: Extending and retracting sounds. --- server/block/piston.go | 5 +++-- server/session/world.go | 4 ++++ server/world/sound/block.go | 6 ++++++ 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/server/block/piston.go b/server/block/piston.go index 91f5589ae..dfe306e25 100644 --- a/server/block/piston.go +++ b/server/block/piston.go @@ -5,6 +5,7 @@ import ( "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" "math/rand" "time" @@ -193,7 +194,7 @@ func (p Piston) push(pos cube.Pos, w *world.World) bool { if p.State == 1 { p.Progress += 0.5 if p.Progress == 0.5 { - // TODO: Out sound! + w.PlaySound(pos.Vec3Centre(), sound.PistonExtend{}) } } @@ -268,7 +269,7 @@ func (p Piston) pull(pos cube.Pos, w *world.World) bool { if p.State == 3 { p.Progress -= 0.5 if p.Progress == 0.5 { - // TODO: In sound! + w.PlaySound(pos.Vec3Centre(), sound.PistonRetract{}) } } diff --git a/server/session/world.go b/server/session/world.go index c167e08fc..521750a1a 100644 --- a/server/session/world.go +++ b/server/session/world.go @@ -747,6 +747,10 @@ func (s *Session) playSound(pos mgl64.Vec3, t world.Sound, disableRelative bool) pk.SoundType = packet.SoundEventComposterFillLayer case sound.ComposterReady: pk.SoundType = packet.SoundEventComposterReady + case sound.PistonExtend: + pk.SoundType = packet.SoundEventPistonOut + case sound.PistonRetract: + pk.SoundType = packet.SoundEventPistonIn } s.writePacket(pk) } diff --git a/server/world/sound/block.go b/server/world/sound/block.go index 35a75c53c..7a2d58500 100644 --- a/server/world/sound/block.go +++ b/server/world/sound/block.go @@ -122,6 +122,12 @@ type ComposterFillLayer struct{ sound } // ComposterReady is a sound played when a composter has produced bone meal and is ready to be collected. type ComposterReady struct{ sound } +// PistonExtend is a sound played when a piston extends. +type PistonExtend struct{ sound } + +// PistonRetract is a sound played when a piston retracts. +type PistonRetract struct{ sound } + // sound implements the world.Sound interface. type sound struct{} From 3b97acd57270dedcbd779d6ff1f69969a26ef82d Mon Sep 17 00:00:00 2001 From: JustTal Date: Wed, 1 Feb 2023 20:07:33 -0600 Subject: [PATCH 53/83] block/lever.go: Sounds! --- server/block/lever.go | 8 +++++++- server/session/world.go | 4 ++++ server/world/sound/block.go | 6 ++++++ 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/server/block/lever.go b/server/block/lever.go index e6f9f1479..1d6d5a68f 100644 --- a/server/block/lever.go +++ b/server/block/lever.go @@ -4,6 +4,7 @@ import ( "github.com/df-mc/dragonfly/server/block/cube" "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" ) @@ -20,7 +21,7 @@ type Lever struct { // Direction is the direction the lever is pointing. This is only used for levers that are attached on up or down // faces. // TODO: Better handle lever direction on up or down faces—using a `cube.Axis` results in a default `Lever` with an - // axis `Y` and a face `Down` which does not map to an existing block state. + // axis `Y` and a face `Down` which does not map to an existing block state. Direction cube.Direction } @@ -82,6 +83,11 @@ func (l Lever) UseOnBlock(pos cube.Pos, face cube.Face, _ mgl64.Vec3, w *world.W func (l Lever) Activate(pos cube.Pos, _ cube.Face, w *world.World, _ item.User, _ *item.UseContext) bool { l.Powered = !l.Powered w.SetBlock(pos, l, nil) + if l.Powered { + w.PlaySound(pos.Vec3Centre(), sound.PowerOn{}) + } else { + w.PlaySound(pos.Vec3Centre(), sound.PowerOff{}) + } updateDirectionalRedstone(pos, w, l.Facing.Opposite()) return true } diff --git a/server/session/world.go b/server/session/world.go index 521750a1a..23fe61cee 100644 --- a/server/session/world.go +++ b/server/session/world.go @@ -747,6 +747,10 @@ func (s *Session) playSound(pos mgl64.Vec3, t world.Sound, disableRelative bool) pk.SoundType = packet.SoundEventComposterFillLayer case sound.ComposterReady: pk.SoundType = packet.SoundEventComposterReady + case sound.PowerOn: + pk.SoundType = packet.SoundEventPowerOn + case sound.PowerOff: + pk.SoundType = packet.SoundEventPowerOff case sound.PistonExtend: pk.SoundType = packet.SoundEventPistonOut case sound.PistonRetract: diff --git a/server/world/sound/block.go b/server/world/sound/block.go index 7a2d58500..a276b3c73 100644 --- a/server/world/sound/block.go +++ b/server/world/sound/block.go @@ -122,6 +122,12 @@ type ComposterFillLayer struct{ sound } // ComposterReady is a sound played when a composter has produced bone meal and is ready to be collected. type ComposterReady struct{ sound } +// PowerOn is a sound played when a redstone component is powered on. +type PowerOn struct{ sound } + +// PowerOff is a sound played when a redstone component is powered off. +type PowerOff struct{ sound } + // PistonExtend is a sound played when a piston extends. type PistonExtend struct{ sound } From 8dce816d18f3ed8b76ccb028f965540a74c8c0e4 Mon Sep 17 00:00:00 2001 From: JustTal Date: Wed, 1 Feb 2023 20:54:13 -0600 Subject: [PATCH 54/83] block/piston_resolver.go: Various fixes. --- server/block/piston_resolver.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/server/block/piston_resolver.go b/server/block/piston_resolver.go index 704f1e443..69246cad0 100644 --- a/server/block/piston_resolver.go +++ b/server/block/piston_resolver.go @@ -70,6 +70,11 @@ func pistonResolve(w *world.World, pos cube.Pos, piston Piston, push bool) *pist // calculateBlocks ... func (r *pistonResolver) calculateBlocks(pos cube.Pos, face cube.Face, breakFace cube.Face) bool { + if pos.Side(breakFace).OutOfBounds(r.w.Range()) { + r.breakPositions = nil + r.attachedPositions = nil + return false + } if _, ok := r.history[pos]; ok { return true } @@ -93,11 +98,6 @@ func (r *pistonResolver) calculateBlocks(pos cube.Pos, face cube.Face, breakFace } return true } - if pos.Side(breakFace).OutOfBounds(r.w.Range()) { - r.breakPositions = nil - r.attachedPositions = nil - return false - } r.attachedPositions = append(r.attachedPositions, pos) if len(r.attachedPositions) >= 13 { From 3c2f65ffc7fb04450df23934ab88682fe97a4b32 Mon Sep 17 00:00:00 2001 From: DaPigGuy Date: Wed, 1 Feb 2023 19:05:48 -0800 Subject: [PATCH 55/83] block/button.go: more redstone inputs! --- cmd/blockhash/main.go | 2 + server/block/button.go | 143 ++++++++++++++++++++++++++++++++++++ server/block/button_type.go | 69 +++++++++++++++++ server/block/hash.go | 5 ++ server/block/register.go | 4 + 5 files changed, 223 insertions(+) create mode 100644 server/block/button.go create mode 100644 server/block/button_type.go diff --git a/cmd/blockhash/main.go b/cmd/blockhash/main.go index a82126baf..b4fcfea26 100644 --- a/cmd/blockhash/main.go +++ b/cmd/blockhash/main.go @@ -213,6 +213,8 @@ func (b *hashBuilder) ftype(structName, s string, expr ast.Expr, directives map[ return "uint64(" + s + ")", 8 case "Block": return s + ".Hash()", 16 + case "ButtonType": + return "uint64(" + s + ".Uint8())", 6 case "Attachment": if _, ok := directives["facing_only"]; ok { log.Println("Found directive: 'facing_only'") diff --git a/server/block/button.go b/server/block/button.go new file mode 100644 index 000000000..187aa4dcd --- /dev/null +++ b/server/block/button.go @@ -0,0 +1,143 @@ +package block + +import ( + "github.com/df-mc/dragonfly/server/block/cube" + "github.com/df-mc/dragonfly/server/item" + "github.com/df-mc/dragonfly/server/world" + "github.com/go-gl/mathgl/mgl64" + "math/rand" + "time" +) + +// TODO: Activate on projectile hit + +// Button is a non-solid block that can provide temporary redstone power. +type Button struct { + empty + transparent + sourceWaterDisplacer + + // Type is the type of the button. + Type ButtonType + // Facing is the face of the block that the button is on. + Facing cube.Face + // Pressed is whether the button is pressed or not. + Pressed bool +} + +// FuelInfo ... +func (b Button) FuelInfo() item.FuelInfo { + if b.Type == StoneButton() || b.Type == PolishedBlackstoneButton() { + return item.FuelInfo{} + } + return newFuelInfo(time.Second * 5) +} + +// Source ... +func (b Button) Source() bool { + return true +} + +// WeakPower ... +func (b Button) WeakPower(cube.Pos, cube.Face, *world.World, bool) int { + if b.Pressed { + return 15 + } + return 0 +} + +// StrongPower ... +func (b Button) StrongPower(_ cube.Pos, face cube.Face, _ *world.World, _ bool) int { + if b.Pressed && b.Facing == face { + return 15 + } + return 0 +} + +// ScheduledTick ... +func (b Button) ScheduledTick(pos cube.Pos, w *world.World, r *rand.Rand) { + if !b.Pressed { + return + } + b.Pressed = false + w.SetBlock(pos, b, nil) + // TODO: Sound + updateDirectionalRedstone(pos, w, b.Facing.Opposite()) +} + +// NeighbourUpdateTick ... +func (b Button) NeighbourUpdateTick(pos, _ cube.Pos, w *world.World) { + if !w.Block(pos.Side(b.Facing.Opposite())).Model().FaceSolid(pos.Side(b.Facing.Opposite()), b.Facing, w) { + w.SetBlock(pos, nil, nil) + dropItem(w, item.NewStack(b, 1), pos.Vec3Centre()) + updateDirectionalRedstone(pos, w, b.Facing.Opposite()) + } +} + +// Activate ... +func (b Button) Activate(pos cube.Pos, _ cube.Face, w *world.World, u item.User, ctx *item.UseContext) bool { + if b.Pressed { + return true + } + b.Pressed = true + w.SetBlock(pos, b, nil) + // TODO: Sound + updateDirectionalRedstone(pos, w, b.Facing.Opposite()) + + delay := time.Millisecond * 1500 + if b.Type == StoneButton() || b.Type == PolishedBlackstoneButton() { + delay = time.Millisecond * 1000 + } + w.ScheduleBlockUpdate(pos, delay) + return true + +} + +// UseOnBlock ... +func (b Button) UseOnBlock(pos cube.Pos, face cube.Face, _ mgl64.Vec3, w *world.World, user item.User, ctx *item.UseContext) bool { + pos, face, used := firstReplaceable(w, pos, face, b) + if !used { + return false + } + if !w.Block(pos.Side(face.Opposite())).Model().FaceSolid(pos.Side(face.Opposite()), face, w) { + return false + } + + b.Facing = face + place(w, pos, b, user, ctx) + return placed(ctx) +} + +// BreakInfo ... +func (b Button) BreakInfo() BreakInfo { + harvestTool := alwaysHarvestable + effectiveTool := axeEffective + if b.Type == StoneButton() || b.Type == PolishedBlackstoneButton() { + harvestTool = pickaxeHarvestable + effectiveTool = pickaxeEffective + } + return newBreakInfo(0.5, harvestTool, effectiveTool, oneOf(b)).withBreakHandler(func(pos cube.Pos, w *world.World, _ item.User) { + updateDirectionalRedstone(pos, w, b.Facing.Opposite()) + }) +} + +// EncodeItem ... +func (b Button) EncodeItem() (name string, meta int16) { + return "minecraft:" + b.Type.String() + "_button", 0 +} + +// EncodeBlock ... +func (b Button) EncodeBlock() (string, map[string]any) { + return "minecraft:" + b.Type.String() + "_button", map[string]any{"facing_direction": int32(b.Facing), "button_pressed_bit": b.Pressed} +} + +// allButtons ... +func allButtons() (buttons []world.Block) { + for _, w := range ButtonTypes() { + for _, f := range cube.Faces() { + buttons = append(buttons, Button{Type: w, Facing: f}) + buttons = append(buttons, Button{Type: w, Facing: f, Pressed: true}) + } + } + return +} diff --git a/server/block/button_type.go b/server/block/button_type.go new file mode 100644 index 000000000..7b6ea050c --- /dev/null +++ b/server/block/button_type.go @@ -0,0 +1,69 @@ +package block + +// ButtonType represents a type of button. +type ButtonType struct { + button + + // Wood is the type of wood of the button. + Wood WoodType +} + +type button uint8 + +// WoodButton returns the wood button type. +func WoodButton(w WoodType) ButtonType { + return ButtonType{0, w} +} + +// StoneButton returns the stone button type. +func StoneButton() ButtonType { + return ButtonType{button: 1} +} + +// PolishedBlackstoneButton returns the polished blackstone button type. +func PolishedBlackstoneButton() ButtonType { + return ButtonType{button: 2} +} + +// Uint8 ... +func (b ButtonType) Uint8() uint8 { + return b.Wood.Uint8() | uint8(b.button)<<4 +} + +// Name ... +func (b ButtonType) Name() string { + switch b.button { + case 0: + return b.Wood.Name() + " Button" + case 1: + return "Stone Button" + case 2: + return "Polished Blackstone Button" + } + panic("unknown button type") +} + +// String ... +func (b ButtonType) String() string { + switch b.button { + case 0: + if b.Wood == OakWood() { + return "wooden" + } + return b.Wood.String() + case 1: + return "stone" + case 2: + return "polished_blackstone" + } + panic("unknown button type") +} + +// ButtonTypes ... +func ButtonTypes() []ButtonType { + types := []ButtonType{StoneButton(), PolishedBlackstoneButton()} + for _, w := range WoodTypes() { + types = append(types, WoodButton(w)) + } + return types +} diff --git a/server/block/hash.go b/server/block/hash.go index 0ec05df01..11f378cab 100644 --- a/server/block/hash.go +++ b/server/block/hash.go @@ -21,6 +21,7 @@ const ( hashBone hashBookshelf hashBricks + hashButton hashCactus hashCake hashCalcite @@ -252,6 +253,10 @@ func (Bricks) Hash() uint64 { return hashBricks } +func (b Button) Hash() uint64 { + return hashButton | uint64(b.Type.Uint8())<<8 | uint64(b.Facing)<<14 | uint64(boolByte(b.Pressed))<<17 +} + func (c Cactus) Hash() uint64 { return hashCactus | uint64(c.Age)<<8 } diff --git a/server/block/register.go b/server/block/register.go index 8f3ea0f95..53c38a6a5 100644 --- a/server/block/register.go +++ b/server/block/register.go @@ -199,6 +199,7 @@ func init() { registerAll(allWater()) registerAll(allWheat()) registerAll(allWood()) + registerAll(allButtons()) registerAll(allWool()) } @@ -367,6 +368,9 @@ func init() { for _, t := range AnvilTypes() { world.RegisterItem(Anvil{Type: t}) } + for _, t := range ButtonTypes() { + world.RegisterItem(Button{Type: t}) + } for _, c := range item.Colours() { world.RegisterItem(Banner{Colour: c}) world.RegisterItem(Carpet{Colour: c}) From f81006f4b3aaee020f21eaf5c72beee8cedc9157 Mon Sep 17 00:00:00 2001 From: DaPigGuy Date: Wed, 1 Feb 2023 19:10:14 -0800 Subject: [PATCH 56/83] block/button_type.go: don't export wood type --- server/block/button_type.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/server/block/button_type.go b/server/block/button_type.go index 7b6ea050c..05771a699 100644 --- a/server/block/button_type.go +++ b/server/block/button_type.go @@ -5,7 +5,7 @@ type ButtonType struct { button // Wood is the type of wood of the button. - Wood WoodType + wood WoodType } type button uint8 @@ -27,14 +27,14 @@ func PolishedBlackstoneButton() ButtonType { // Uint8 ... func (b ButtonType) Uint8() uint8 { - return b.Wood.Uint8() | uint8(b.button)<<4 + return b.wood.Uint8() | uint8(b.button)<<4 } // Name ... func (b ButtonType) Name() string { switch b.button { case 0: - return b.Wood.Name() + " Button" + return b.wood.Name() + " Button" case 1: return "Stone Button" case 2: @@ -47,10 +47,10 @@ func (b ButtonType) Name() string { func (b ButtonType) String() string { switch b.button { case 0: - if b.Wood == OakWood() { + if b.wood == OakWood() { return "wooden" } - return b.Wood.String() + return b.wood.String() case 1: return "stone" case 2: From b6f1d0ba697723d7f11be53182d58d5f6b1fd3bb Mon Sep 17 00:00:00 2001 From: DaPigGuy Date: Wed, 1 Feb 2023 19:11:01 -0800 Subject: [PATCH 57/83] block/button.go: add sounds --- server/block/button.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/server/block/button.go b/server/block/button.go index 187aa4dcd..ee38fc8fd 100644 --- a/server/block/button.go +++ b/server/block/button.go @@ -4,6 +4,7 @@ import ( "github.com/df-mc/dragonfly/server/block/cube" "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" "math/rand" "time" @@ -61,7 +62,7 @@ func (b Button) ScheduledTick(pos cube.Pos, w *world.World, r *rand.Rand) { } b.Pressed = false w.SetBlock(pos, b, nil) - // TODO: Sound + w.PlaySound(pos.Vec3Centre(), sound.PowerOff{}) updateDirectionalRedstone(pos, w, b.Facing.Opposite()) } @@ -81,7 +82,7 @@ func (b Button) Activate(pos cube.Pos, _ cube.Face, w *world.World, u item.User, } b.Pressed = true w.SetBlock(pos, b, nil) - // TODO: Sound + w.PlaySound(pos.Vec3Centre(), sound.PowerOn{}) updateDirectionalRedstone(pos, w, b.Facing.Opposite()) delay := time.Millisecond * 1500 From 2dcfaecac1cf693d3b368b1f8649c9322d455ceb Mon Sep 17 00:00:00 2001 From: JustTal Date: Wed, 1 Feb 2023 21:32:29 -0600 Subject: [PATCH 58/83] block: Made repeaters and beacons waterloggable. --- server/block/beacon.go | 2 +- server/block/redstone_repeater.go | 7 ++++--- server/block/redstone_wire.go | 3 +++ server/block/torch.go | 2 +- 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/server/block/beacon.go b/server/block/beacon.go index d671d3ea0..b672bcc5e 100644 --- a/server/block/beacon.go +++ b/server/block/beacon.go @@ -75,7 +75,7 @@ func (b Beacon) EncodeNBT() map[string]any { } // SideClosed ... -func (b Beacon) SideClosed(cube.Pos, cube.Pos, *world.World) bool { +func (Beacon) SideClosed(cube.Pos, cube.Pos, *world.World) bool { return false } diff --git a/server/block/redstone_repeater.go b/server/block/redstone_repeater.go index 860467aa4..077439eba 100644 --- a/server/block/redstone_repeater.go +++ b/server/block/redstone_repeater.go @@ -14,6 +14,7 @@ import ( // signals, prevent signals moving backwards, or to "lock" signals in one state. type RedstoneRepeater struct { transparent + flowingWaterDisplacer // Facing is the direction from the torch to the block. Facing cube.Direction @@ -23,9 +24,9 @@ type RedstoneRepeater struct { Delay int } -// HasLiquidDrops ... -func (RedstoneRepeater) HasLiquidDrops() bool { - return true +// SideClosed ... +func (RedstoneRepeater) SideClosed(cube.Pos, cube.Pos, *world.World) bool { + return false } // Model ... diff --git a/server/block/redstone_wire.go b/server/block/redstone_wire.go index 02a606639..2d3f2cd38 100644 --- a/server/block/redstone_wire.go +++ b/server/block/redstone_wire.go @@ -50,6 +50,9 @@ func (r RedstoneWire) UseOnBlock(pos cube.Pos, face cube.Face, _ mgl64.Vec3, w * if !used { return } + if _, ok := w.Liquid(pos); ok { + return false + } belowPos := pos.Side(cube.FaceDown) if !w.Block(belowPos).Model().FaceSolid(belowPos, cube.FaceUp, w) { return diff --git a/server/block/torch.go b/server/block/torch.go index 93591b838..f6671d45b 100644 --- a/server/block/torch.go +++ b/server/block/torch.go @@ -42,7 +42,7 @@ func (t Torch) UseOnBlock(pos cube.Pos, face cube.Face, _ mgl64.Vec3, w *world.W if face == cube.FaceDown { return false } - if _, ok := w.Block(pos).(world.Liquid); ok { + if _, ok := w.Liquid(pos); ok { return false } if !w.Block(pos.Side(face.Opposite())).Model().FaceSolid(pos.Side(face.Opposite()), face, w) { From d1d2a16badc0f666db460fd5fb5d6634bc8aab9e Mon Sep 17 00:00:00 2001 From: DaPigGuy Date: Wed, 1 Feb 2023 19:35:23 -0800 Subject: [PATCH 59/83] block/jukebox.go: output redstone power with disc inserted --- server/block/jukebox.go | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/server/block/jukebox.go b/server/block/jukebox.go index 36bb2057f..07472fbbd 100644 --- a/server/block/jukebox.go +++ b/server/block/jukebox.go @@ -19,6 +19,24 @@ type Jukebox struct { Item item.Stack } +// Source ... +func (j Jukebox) Source() bool { + return true +} + +// WeakPower ... +func (j Jukebox) WeakPower(cube.Pos, cube.Face, *world.World, bool) int { + if j.Item.Empty() { + return 0 + } + return 15 +} + +// StrongPower ... +func (j Jukebox) StrongPower(cube.Pos, cube.Face, *world.World, bool) int { + return 0 +} + // FuelInfo ... func (j Jukebox) FuelInfo() item.FuelInfo { return newFuelInfo(time.Second * 15) From a0d764f7eab807b7f857cf70a8fcc3f5d70ca806 Mon Sep 17 00:00:00 2001 From: JustTal Date: Wed, 1 Feb 2023 21:35:39 -0600 Subject: [PATCH 60/83] block: Made pistons waterloggable. --- server/block/piston.go | 6 ++++++ server/block/piston_arm_collision.go | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/server/block/piston.go b/server/block/piston.go index dfe306e25..3694f9eea 100644 --- a/server/block/piston.go +++ b/server/block/piston.go @@ -15,6 +15,7 @@ import ( type Piston struct { solid transparent + sourceWaterDisplacer // Facing represents the direction the piston is facing. Facing cube.Face @@ -42,6 +43,11 @@ func (p Piston) BreakInfo() BreakInfo { return newBreakInfo(1.5, alwaysHarvestable, pickaxeEffective, oneOf(Piston{Sticky: p.Sticky})) } +// SideClosed ... +func (Piston) SideClosed(cube.Pos, cube.Pos, *world.World) bool { + return false +} + // EncodeItem ... func (p Piston) EncodeItem() (name string, meta int16) { if p.Sticky { diff --git a/server/block/piston_arm_collision.go b/server/block/piston_arm_collision.go index 6125a4963..ce5943a4d 100644 --- a/server/block/piston_arm_collision.go +++ b/server/block/piston_arm_collision.go @@ -10,6 +10,7 @@ import ( type PistonArmCollision struct { empty transparent + sourceWaterDisplacer // Facing represents the direction the piston is facing. Facing cube.Face @@ -20,6 +21,11 @@ func (PistonArmCollision) PistonImmovable() bool { return true } +// SideClosed ... +func (PistonArmCollision) SideClosed(cube.Pos, cube.Pos, *world.World) bool { + return false +} + // EncodeBlock ... func (c PistonArmCollision) EncodeBlock() (string, map[string]any) { return "minecraft:piston_arm_collision", map[string]any{"facing_direction": int32(c.Facing)} From 517046d8ae3b2a3e97140cc3e390f0e560a87e48 Mon Sep 17 00:00:00 2001 From: DaPigGuy Date: Wed, 1 Feb 2023 19:57:40 -0800 Subject: [PATCH 61/83] block/jukebox.go: update redstone power on break --- server/block/jukebox.go | 1 + 1 file changed, 1 insertion(+) diff --git a/server/block/jukebox.go b/server/block/jukebox.go index 07472fbbd..2ee87f8b0 100644 --- a/server/block/jukebox.go +++ b/server/block/jukebox.go @@ -52,6 +52,7 @@ func (j Jukebox) BreakInfo() BreakInfo { if _, hasDisc := j.Disc(); hasDisc { w.PlaySound(pos.Vec3(), sound.MusicDiscEnd{}) } + updateAroundRedstone(pos, w) }) } From 87779b6f19785d54a388e6dbb1609f437d5d7142 Mon Sep 17 00:00:00 2001 From: DaPigGuy Date: Wed, 1 Feb 2023 20:20:10 -0800 Subject: [PATCH 62/83] block/observer.go: implement observers --- server/block/hash.go | 5 ++ server/block/observer.go | 98 ++++++++++++++++++++++++++++++++++++++++ server/block/register.go | 4 +- 3 files changed, 106 insertions(+), 1 deletion(-) create mode 100644 server/block/observer.go diff --git a/server/block/hash.go b/server/block/hash.go index 11f378cab..96397ca29 100644 --- a/server/block/hash.go +++ b/server/block/hash.go @@ -115,6 +115,7 @@ const ( hashNetherite hashNetherrack hashNote + hashObserver hashObsidian hashPackedIce hashPackedMud @@ -629,6 +630,10 @@ func (Note) Hash() uint64 { return hashNote } +func (d Observer) Hash() uint64 { + return hashObserver | uint64(d.Facing)<<8 | uint64(boolByte(d.Powered))<<11 +} + func (o Obsidian) Hash() uint64 { return hashObsidian | uint64(boolByte(o.Crying))<<8 } diff --git a/server/block/observer.go b/server/block/observer.go new file mode 100644 index 000000000..f06e12f87 --- /dev/null +++ b/server/block/observer.go @@ -0,0 +1,98 @@ +package block + +import ( + "github.com/df-mc/dragonfly/server/block/cube" + "github.com/df-mc/dragonfly/server/item" + "github.com/df-mc/dragonfly/server/world" + "github.com/go-gl/mathgl/mgl64" + "math/rand" + "time" +) + +// Observer is a block that emits a redstone signal when the block or fluid it faces experiences a change. +type Observer struct { + solid + + // Facing is the direction the observer is observing. + Facing cube.Face + // Powered is whether the observer is powered or not. + Powered bool +} + +// Source ... +func (d Observer) Source() bool { + return true +} + +// WeakPower ... +func (d Observer) WeakPower(pos cube.Pos, face cube.Face, w *world.World, accountForDust bool) int { + return d.StrongPower(pos, face, w, accountForDust) +} + +// StrongPower ... +func (d Observer) StrongPower(_ cube.Pos, face cube.Face, w *world.World, _ bool) int { + if !d.Powered || face != d.Facing { + return 0 + } + return 15 +} + +// ScheduledTick ... +func (d Observer) ScheduledTick(pos cube.Pos, w *world.World, _ *rand.Rand) { + if !d.Powered { + w.ScheduleBlockUpdate(pos, time.Millisecond*100) + } + d.Powered = !d.Powered + w.SetBlock(pos, d, nil) + updateDirectionalRedstone(pos, w, d.Facing.Opposite()) +} + +// NeighbourUpdateTick ... +func (d Observer) NeighbourUpdateTick(pos, changedNeighbour cube.Pos, w *world.World) { + if pos.Side(d.Facing) != changedNeighbour { + return + } + if d.Powered { + return + } + w.ScheduleBlockUpdate(pos, time.Millisecond*100) + updateDirectionalRedstone(pos, w, d.Facing.Opposite()) +} + +// UseOnBlock ... +func (d Observer) UseOnBlock(pos cube.Pos, face cube.Face, _ mgl64.Vec3, w *world.World, user item.User, ctx *item.UseContext) bool { + pos, _, used := firstReplaceable(w, pos, face, d) + if !used { + return false + } + d.Facing = calculateAnySidedFace(user, pos, false) + + place(w, pos, d, user, ctx) + return placed(ctx) +} + +// BreakInfo ... +func (d Observer) BreakInfo() BreakInfo { + return newBreakInfo(3, pickaxeHarvestable, pickaxeEffective, oneOf(d)).withBreakHandler(func(pos cube.Pos, w *world.World, _ item.User) { + updateDirectionalRedstone(pos, w, d.Facing.Opposite()) + }) +} + +// EncodeItem ... +func (d Observer) EncodeItem() (name string, meta int16) { + return "minecraft:observer", 0 +} + +// EncodeBlock ... +func (d Observer) EncodeBlock() (string, map[string]any) { + return "minecraft:observer", map[string]any{"facing_direction": int32(d.Facing), "powered_bit": d.Powered} +} + +// allObservers ... +func allObservers() (observers []world.Block) { + for _, f := range cube.Faces() { + observers = append(observers, Observer{Facing: f}) + observers = append(observers, Observer{Facing: f, Powered: true}) + } + return +} diff --git a/server/block/register.go b/server/block/register.go index 53c38a6a5..a013a83b2 100644 --- a/server/block/register.go +++ b/server/block/register.go @@ -124,6 +124,7 @@ func init() { registerAll(allBlackstone()) registerAll(allBlastFurnaces()) registerAll(allBoneBlock()) + registerAll(allButtons()) registerAll(allCactus()) registerAll(allCake()) registerAll(allCarpet()) @@ -166,6 +167,7 @@ func init() { registerAll(allMuddyMangroveRoots()) registerAll(allNetherBricks()) registerAll(allNetherWart()) + registerAll(allObservers()) registerAll(allPistonArmCollisions()) registerAll(allPistons()) registerAll(allPlanks()) @@ -199,7 +201,6 @@ func init() { registerAll(allWater()) registerAll(allWheat()) registerAll(allWood()) - registerAll(allButtons()) registerAll(allWool()) } @@ -295,6 +296,7 @@ func init() { world.RegisterItem(Netherite{}) world.RegisterItem(Netherrack{}) world.RegisterItem(Note{Pitch: 24}) + world.RegisterItem(Observer{}) world.RegisterItem(Obsidian{Crying: true}) world.RegisterItem(Obsidian{}) world.RegisterItem(PackedIce{}) From 47a32f69a195cb7e47d19da99bbeb3efa9a6f299 Mon Sep 17 00:00:00 2001 From: DaPigGuy Date: Wed, 1 Feb 2023 20:36:23 -0800 Subject: [PATCH 63/83] block/observer.go: "fix" Y-axis facing observer placement --- server/block/observer.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/server/block/observer.go b/server/block/observer.go index f06e12f87..dd87d2d6e 100644 --- a/server/block/observer.go +++ b/server/block/observer.go @@ -66,6 +66,9 @@ func (d Observer) UseOnBlock(pos cube.Pos, face cube.Face, _ mgl64.Vec3, w *worl return false } d.Facing = calculateAnySidedFace(user, pos, false) + if d.Facing.Axis() == cube.Y { + d.Facing = d.Facing.Opposite() + } place(w, pos, d, user, ctx) return placed(ctx) From 4311b8e44d3bb1e9f7d0afeb6e740b29121801b1 Mon Sep 17 00:00:00 2001 From: JustTal Date: Wed, 1 Feb 2023 23:10:01 -0600 Subject: [PATCH 64/83] block: Added slime. --- server/block/hash.go | 5 ++++ server/block/piston_resolver.go | 15 +++++++++- server/block/register.go | 2 ++ server/block/slime.go | 51 +++++++++++++++++++++++++++++++++ 4 files changed, 72 insertions(+), 1 deletion(-) create mode 100644 server/block/slime.go diff --git a/server/block/hash.go b/server/block/hash.go index 96397ca29..2665cfe22 100644 --- a/server/block/hash.go +++ b/server/block/hash.go @@ -151,6 +151,7 @@ const ( hashSign hashSkull hashSlab + hashSlime hashSmithingTable hashSmoker hashSnow @@ -774,6 +775,10 @@ func (s Slab) Hash() uint64 { return hashSlab | s.Block.Hash()<<8 | uint64(boolByte(s.Top))<<24 | uint64(boolByte(s.Double))<<25 } +func (Slime) Hash() uint64 { + return hashSlime +} + func (SmithingTable) Hash() uint64 { return hashSmithingTable } diff --git a/server/block/piston_resolver.go b/server/block/piston_resolver.go index 69246cad0..4ea960584 100644 --- a/server/block/piston_resolver.go +++ b/server/block/piston_resolver.go @@ -105,7 +105,20 @@ func (r *pistonResolver) calculateBlocks(pos cube.Pos, face cube.Face, breakFace r.attachedPositions = nil return false } - return r.calculateBlocks(pos.Side(breakFace), breakFace, breakFace) + + if _, ok := block.(Slime); ok { + for _, otherFace := range cube.Faces() { + if face.Opposite() == otherFace { + continue + } + if !r.calculateBlocks(pos.Side(otherFace), otherFace, breakFace) { + return false + } + } + } else if !r.calculateBlocks(pos.Side(breakFace), breakFace, breakFace) { + return false + } + return true } // canMove ... diff --git a/server/block/register.go b/server/block/register.go index a013a83b2..46a3df8ff 100644 --- a/server/block/register.go +++ b/server/block/register.go @@ -93,6 +93,7 @@ func init() { world.RegisterBlock(Sand{}) world.RegisterBlock(SeaLantern{}) world.RegisterBlock(Shroomlight{}) + world.RegisterBlock(Slime{}) world.RegisterBlock(SmithingTable{}) world.RegisterBlock(Snow{}) world.RegisterBlock(SoulSand{}) @@ -331,6 +332,7 @@ func init() { world.RegisterItem(SeaLantern{}) world.RegisterItem(SeaPickle{}) world.RegisterItem(Shroomlight{}) + world.RegisterItem(Slime{}) world.RegisterItem(SmithingTable{}) world.RegisterItem(Smoker{}) world.RegisterItem(Snow{}) diff --git a/server/block/slime.go b/server/block/slime.go new file mode 100644 index 000000000..13884595b --- /dev/null +++ b/server/block/slime.go @@ -0,0 +1,51 @@ +package block + +import ( + "github.com/df-mc/dragonfly/server/block/cube" + "github.com/df-mc/dragonfly/server/world" + "github.com/go-gl/mathgl/mgl64" +) + +// Slime is a storage block equivalent to nine slimeballs. It has both sticky and bouncy properties making it useful in +// conjunction with pistons to move both blocks and entities. +type Slime struct { + solid + transparent +} + +// BreakInfo ... +func (s Slime) BreakInfo() BreakInfo { + return newBreakInfo(0, alwaysHarvestable, nothingEffective, oneOf(s)) +} + +// Friction ... +func (Slime) Friction() float64 { + return 0.8 +} + +// EncodeItem ... +func (Slime) EncodeItem() (name string, meta int16) { + return "minecraft:slime", 0 +} + +// EncodeBlock ... +func (Slime) EncodeBlock() (name string, properties map[string]any) { + return "minecraft:slime", nil +} + +// EntityLand ... +func (Slime) EntityLand(_ cube.Pos, _ *world.World, e world.Entity, distance *float64) { + if s, ok := e.(interface { + Sneaking() bool + }); !ok || !s.Sneaking() { + *distance = 0 + } + if v, ok := e.(interface { + Velocity() mgl64.Vec3 + SetVelocity(mgl64.Vec3) + }); ok { + vel := v.Velocity() + vel[1] = -vel[1] + v.SetVelocity(vel) + } +} From 89e9cfb9d4b935f99d1df7d2a7f7ccbb29722bcc Mon Sep 17 00:00:00 2001 From: DaPigGuy Date: Thu, 2 Feb 2023 00:37:50 -0800 Subject: [PATCH 65/83] block/redstone.go: prevent CI failure --- server/block/redstone.go | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/server/block/redstone.go b/server/block/redstone.go index d4451bf5f..3571c88bd 100644 --- a/server/block/redstone.go +++ b/server/block/redstone.go @@ -3,7 +3,6 @@ package block import ( "github.com/df-mc/dragonfly/server/block/cube" "github.com/df-mc/dragonfly/server/block/model" - "github.com/df-mc/dragonfly/server/item/inventory" "github.com/df-mc/dragonfly/server/world" "golang.org/x/exp/slices" ) @@ -32,21 +31,6 @@ type PistonBreakable interface { PistonBreakable() bool } -// inventoryComparatorOutput calculates the output of a comparator based on the contents of an inventory. -func inventoryComparatorOutput(inv *inventory.Inventory) int { - if inv.Empty() { - return 0 - } - var amount int - for _, st := range inv.Slots() { - if st.Empty() { - continue - } - amount += st.Count() / st.MaxCount() - } - return (amount/inv.Size())*14 + 1 -} - // wireNetwork implements a minimally-invasive bolt-on accelerator that performs a breadth-first search through redstone // wires in order to more efficiently and compute new redstone wire power levels and determine the order in which other // blocks should be updated. This implementation is heavily based off of RedstoneWireTurbo and MCHPRS. From 1d0043f8803ce584cadbce5e8b0c751cd8b60ec4 Mon Sep 17 00:00:00 2001 From: JustTal Date: Thu, 2 Feb 2023 20:00:17 -0600 Subject: [PATCH 66/83] world/world.go: Pass block state to ScheduleBlockUpdate. --- server/block/button.go | 2 +- server/block/composter.go | 2 +- server/block/coral.go | 2 +- server/block/coral_block.go | 2 +- server/block/fire.go | 4 +-- server/block/hash.go | 4 +-- server/block/lava.go | 2 +- server/block/observer.go | 55 +++++++++++++++---------------- server/block/piston.go | 6 ++-- server/block/redstone_lamp.go | 2 +- server/block/redstone_repeater.go | 4 +-- server/block/redstone_torch.go | 2 +- server/block/water.go | 4 +-- server/item/fire_charge.go | 2 +- server/item/flint_and_steel.go | 2 +- server/world/conf.go | 2 +- server/world/provider.go | 16 +++++++-- server/world/tick.go | 39 ++++++++++++---------- server/world/world.go | 21 ++++++++++-- 19 files changed, 102 insertions(+), 71 deletions(-) diff --git a/server/block/button.go b/server/block/button.go index ee38fc8fd..c5c04dab0 100644 --- a/server/block/button.go +++ b/server/block/button.go @@ -89,7 +89,7 @@ func (b Button) Activate(pos cube.Pos, _ cube.Face, w *world.World, u item.User, if b.Type == StoneButton() || b.Type == PolishedBlackstoneButton() { delay = time.Millisecond * 1000 } - w.ScheduleBlockUpdate(pos, delay) + w.ScheduleBlockUpdate(b, pos, delay) return true } diff --git a/server/block/composter.go b/server/block/composter.go index a309a38d0..cbe3ce28f 100644 --- a/server/block/composter.go +++ b/server/block/composter.go @@ -77,7 +77,7 @@ func (c Composter) Activate(pos cube.Pos, _ cube.Face, w *world.World, u item.Us w.SetBlock(pos, c, nil) w.PlaySound(pos.Vec3(), sound.ComposterFillLayer{}) if c.Level == 7 { - w.ScheduleBlockUpdate(pos, time.Second) + w.ScheduleBlockUpdate(c, pos, time.Second) } return true } diff --git a/server/block/coral.go b/server/block/coral.go index fb139a0d8..652f9d884 100644 --- a/server/block/coral.go +++ b/server/block/coral.go @@ -64,7 +64,7 @@ func (c Coral) NeighbourUpdateTick(pos, _ cube.Pos, w *world.World) { if c.Dead { return } - w.ScheduleBlockUpdate(pos, time.Second*5/2) + w.ScheduleBlockUpdate(c, pos, time.Second*5/2) } // ScheduledTick ... diff --git a/server/block/coral_block.go b/server/block/coral_block.go index f351b0c32..42239639a 100644 --- a/server/block/coral_block.go +++ b/server/block/coral_block.go @@ -23,7 +23,7 @@ func (c CoralBlock) NeighbourUpdateTick(pos, _ cube.Pos, w *world.World) { if c.Dead { return } - w.ScheduleBlockUpdate(pos, time.Second*5/2) + w.ScheduleBlockUpdate(c, pos, time.Second*5/2) } // ScheduledTick ... diff --git a/server/block/fire.go b/server/block/fire.go index 2f986d585..bffdbb109 100644 --- a/server/block/fire.go +++ b/server/block/fire.go @@ -105,7 +105,7 @@ func (f Fire) tick(pos cube.Pos, w *world.World, r *rand.Rand) { w.SetBlock(pos, f, nil) } - w.ScheduleBlockUpdate(pos, time.Duration(30+r.Intn(10))*time.Second/20) + w.ScheduleBlockUpdate(f, pos, time.Duration(30+r.Intn(10))*time.Second/20) if !infinitelyBurns { _, waterBelow := w.Block(pos.Side(cube.FaceDown)).(Water) @@ -193,7 +193,7 @@ func (f Fire) spread(from, to cube.Pos, w *world.World, r *rand.Rand) { return } w.SetBlock(to, Fire{Type: f.Type, Age: min(15, f.Age+r.Intn(5)/4)}, nil) - w.ScheduleBlockUpdate(to, time.Duration(30+r.Intn(10))*time.Second/20) + w.ScheduleBlockUpdate(f, to, time.Duration(30+r.Intn(10))*time.Second/20) } // EntityInside ... diff --git a/server/block/hash.go b/server/block/hash.go index 2665cfe22..0277f49c2 100644 --- a/server/block/hash.go +++ b/server/block/hash.go @@ -631,8 +631,8 @@ func (Note) Hash() uint64 { return hashNote } -func (d Observer) Hash() uint64 { - return hashObserver | uint64(d.Facing)<<8 | uint64(boolByte(d.Powered))<<11 +func (o Observer) Hash() uint64 { + return hashObserver | uint64(o.Facing)<<8 | uint64(boolByte(o.Powered))<<11 } func (o Obsidian) Hash() uint64 { diff --git a/server/block/lava.go b/server/block/lava.go index 1610519c6..eb86a03b8 100644 --- a/server/block/lava.go +++ b/server/block/lava.go @@ -95,7 +95,7 @@ func (Lava) PistonBreakable() bool { // NeighbourUpdateTick ... func (l Lava) NeighbourUpdateTick(pos, _ cube.Pos, w *world.World) { if !l.Harden(pos, w, nil) { - w.ScheduleBlockUpdate(pos, w.Dimension().LavaSpreadDuration()) + w.ScheduleBlockUpdate(l, pos, w.Dimension().LavaSpreadDuration()) } } diff --git a/server/block/observer.go b/server/block/observer.go index dd87d2d6e..26236908a 100644 --- a/server/block/observer.go +++ b/server/block/observer.go @@ -20,75 +20,74 @@ type Observer struct { } // Source ... -func (d Observer) Source() bool { +func (o Observer) Source() bool { return true } // WeakPower ... -func (d Observer) WeakPower(pos cube.Pos, face cube.Face, w *world.World, accountForDust bool) int { - return d.StrongPower(pos, face, w, accountForDust) +func (o Observer) WeakPower(pos cube.Pos, face cube.Face, w *world.World, accountForDust bool) int { + return o.StrongPower(pos, face, w, accountForDust) } // StrongPower ... -func (d Observer) StrongPower(_ cube.Pos, face cube.Face, w *world.World, _ bool) int { - if !d.Powered || face != d.Facing { +func (o Observer) StrongPower(_ cube.Pos, face cube.Face, w *world.World, _ bool) int { + if !o.Powered || face != o.Facing { return 0 } return 15 } // ScheduledTick ... -func (d Observer) ScheduledTick(pos cube.Pos, w *world.World, _ *rand.Rand) { - if !d.Powered { - w.ScheduleBlockUpdate(pos, time.Millisecond*100) +func (o Observer) ScheduledTick(pos cube.Pos, w *world.World, _ *rand.Rand) { + o.Powered = !o.Powered + if o.Powered { + w.ScheduleBlockUpdate(o, pos, time.Millisecond*100) } - d.Powered = !d.Powered - w.SetBlock(pos, d, nil) - updateDirectionalRedstone(pos, w, d.Facing.Opposite()) + w.SetBlock(pos, o, nil) + updateDirectionalRedstone(pos, w, o.Facing.Opposite()) } // NeighbourUpdateTick ... -func (d Observer) NeighbourUpdateTick(pos, changedNeighbour cube.Pos, w *world.World) { - if pos.Side(d.Facing) != changedNeighbour { +func (o Observer) NeighbourUpdateTick(pos, changedNeighbour cube.Pos, w *world.World) { + if pos.Side(o.Facing) != changedNeighbour { return } - if d.Powered { + if o.Powered { return } - w.ScheduleBlockUpdate(pos, time.Millisecond*100) - updateDirectionalRedstone(pos, w, d.Facing.Opposite()) + w.ScheduleBlockUpdate(o, pos, time.Millisecond*100) } // UseOnBlock ... -func (d Observer) UseOnBlock(pos cube.Pos, face cube.Face, _ mgl64.Vec3, w *world.World, user item.User, ctx *item.UseContext) bool { - pos, _, used := firstReplaceable(w, pos, face, d) +func (o Observer) UseOnBlock(pos cube.Pos, face cube.Face, _ mgl64.Vec3, w *world.World, user item.User, ctx *item.UseContext) bool { + pos, _, used := firstReplaceable(w, pos, face, o) if !used { return false } - d.Facing = calculateAnySidedFace(user, pos, false) - if d.Facing.Axis() == cube.Y { - d.Facing = d.Facing.Opposite() + o.Facing = calculateAnySidedFace(user, pos, false) + if o.Facing.Axis() == cube.Y { + o.Facing = o.Facing.Opposite() } - place(w, pos, d, user, ctx) + place(w, pos, o, user, ctx) return placed(ctx) } // BreakInfo ... -func (d Observer) BreakInfo() BreakInfo { - return newBreakInfo(3, pickaxeHarvestable, pickaxeEffective, oneOf(d)).withBreakHandler(func(pos cube.Pos, w *world.World, _ item.User) { - updateDirectionalRedstone(pos, w, d.Facing.Opposite()) +func (o Observer) BreakInfo() BreakInfo { + return newBreakInfo(3, pickaxeHarvestable, pickaxeEffective, oneOf(o)).withBreakHandler(func(pos cube.Pos, w *world.World, _ item.User) { + updateDirectionalRedstone(pos, w, o.Facing.Opposite()) }) } // EncodeItem ... -func (d Observer) EncodeItem() (name string, meta int16) { +func (o Observer) EncodeItem() (name string, meta int16) { return "minecraft:observer", 0 } // EncodeBlock ... -func (d Observer) EncodeBlock() (string, map[string]any) { - return "minecraft:observer", map[string]any{"facing_direction": int32(d.Facing), "powered_bit": d.Powered} +func (o Observer) EncodeBlock() (string, map[string]any) { + return "minecraft:observer", map[string]any{"facing_direction": int32(o.Facing), "powered_bit": o.Powered} } // allObservers ... diff --git a/server/block/piston.go b/server/block/piston.go index 3694f9eea..705244150 100644 --- a/server/block/piston.go +++ b/server/block/piston.go @@ -137,8 +137,8 @@ func (p Piston) DecodeNBT(m map[string]any) any { } // RedstoneUpdate ... -func (Piston) RedstoneUpdate(pos cube.Pos, w *world.World) { - w.ScheduleBlockUpdate(pos, time.Millisecond*50) +func (p Piston) RedstoneUpdate(pos cube.Pos, w *world.World) { + w.ScheduleBlockUpdate(p, pos, time.Millisecond*50) } // ScheduledTick ... @@ -150,7 +150,7 @@ func (p Piston) ScheduledTick(pos cube.Pos, w *world.World, _ *rand.Rand) { } else if !p.pull(pos, w) { return } - w.ScheduleBlockUpdate(pos, time.Millisecond*50) + w.ScheduleBlockUpdate(p, pos, time.Millisecond*50) } // armFace ... diff --git a/server/block/redstone_lamp.go b/server/block/redstone_lamp.go index dcf1c60d8..6fd2a0bb4 100644 --- a/server/block/redstone_lamp.go +++ b/server/block/redstone_lamp.go @@ -63,7 +63,7 @@ func (l RedstoneLamp) RedstoneUpdate(pos cube.Pos, w *world.World) { l.Lit = true w.SetBlock(pos, l, &world.SetOpts{DisableBlockUpdates: true}) } else { - w.ScheduleBlockUpdate(pos, time.Millisecond*200) + w.ScheduleBlockUpdate(l, pos, time.Millisecond*200) } } diff --git a/server/block/redstone_repeater.go b/server/block/redstone_repeater.go index 077439eba..621b525b7 100644 --- a/server/block/redstone_repeater.go +++ b/server/block/redstone_repeater.go @@ -101,7 +101,7 @@ func (r RedstoneRepeater) RedstoneUpdate(pos cube.Pos, w *world.World) { return } if r.inputStrength(pos, w) > 0 != r.Powered { - w.ScheduleBlockUpdate(pos, time.Duration(r.Delay+1)*time.Millisecond*100) + w.ScheduleBlockUpdate(r, pos, time.Duration(r.Delay+1)*time.Millisecond*100) } } @@ -117,7 +117,7 @@ func (r RedstoneRepeater) ScheduledTick(pos cube.Pos, w *world.World, _ *rand.Ra updateGateRedstone(pos, w, r.Facing.Face().Opposite()) if r.Powered && r.inputStrength(pos, w) <= 0 { - w.ScheduleBlockUpdate(pos, time.Duration(r.Delay+1)*time.Millisecond*100) + w.ScheduleBlockUpdate(r, pos, time.Duration(r.Delay+1)*time.Millisecond*100) } w.SetBlock(pos, r, nil) updateGateRedstone(pos, w, r.Facing.Face()) diff --git a/server/block/redstone_torch.go b/server/block/redstone_torch.go index c36a97387..8a0ae5c75 100644 --- a/server/block/redstone_torch.go +++ b/server/block/redstone_torch.go @@ -92,7 +92,7 @@ func (t RedstoneTorch) RedstoneUpdate(pos cube.Pos, w *world.World) { if t.inputStrength(pos, w) > 0 != t.Lit { return } - w.ScheduleBlockUpdate(pos, time.Millisecond*100) + w.ScheduleBlockUpdate(t, pos, time.Millisecond*100) } // ScheduledTick ... diff --git a/server/block/water.go b/server/block/water.go index 478f835b9..fb5748c70 100644 --- a/server/block/water.go +++ b/server/block/water.go @@ -118,13 +118,13 @@ func (w Water) ScheduledTick(pos cube.Pos, wo *world.World, _ *rand.Rand) { } // NeighbourUpdateTick ... -func (Water) NeighbourUpdateTick(pos, _ cube.Pos, wo *world.World) { +func (w Water) NeighbourUpdateTick(pos, _ cube.Pos, wo *world.World) { if wo.Dimension().WaterEvaporates() { // Particles are spawned client-side. wo.SetLiquid(pos, nil) return } - wo.ScheduleBlockUpdate(pos, time.Second/4) + wo.ScheduleBlockUpdate(w, pos, time.Second/4) } // LiquidType ... diff --git a/server/item/fire_charge.go b/server/item/fire_charge.go index ceffd7ace..3c583588c 100644 --- a/server/item/fire_charge.go +++ b/server/item/fire_charge.go @@ -28,7 +28,7 @@ func (f FireCharge) UseOnBlock(pos cube.Pos, face cube.Face, _ mgl64.Vec3, w *wo ctx.SubtractFromCount(1) w.PlaySound(s.Vec3Centre(), sound.FireCharge{}) w.SetBlock(s, fire(), nil) - w.ScheduleBlockUpdate(s, time.Duration(30+rand.Intn(10))*time.Second/20) + w.ScheduleBlockUpdate(w.Block(s), s, time.Duration(30+rand.Intn(10))*time.Second/20) return true } return false diff --git a/server/item/flint_and_steel.go b/server/item/flint_and_steel.go index 2e7c19bfd..3d1439913 100644 --- a/server/item/flint_and_steel.go +++ b/server/item/flint_and_steel.go @@ -39,7 +39,7 @@ func (f FlintAndSteel) UseOnBlock(pos cube.Pos, face cube.Face, _ mgl64.Vec3, w } else if s := pos.Side(face); w.Block(s) == air() { w.PlaySound(s.Vec3Centre(), sound.Ignite{}) w.SetBlock(s, fire(), nil) - w.ScheduleBlockUpdate(s, time.Duration(30+rand.Intn(10))*time.Second/20) + w.ScheduleBlockUpdate(w.Block(s), s, time.Duration(30+rand.Intn(10))*time.Second/20) return true } return false diff --git a/server/world/conf.go b/server/world/conf.go index a6b9ca905..4a3d54b72 100644 --- a/server/world/conf.go +++ b/server/world/conf.go @@ -71,7 +71,7 @@ func (conf Config) New() *World { } s := conf.Provider.Settings() w := &World{ - scheduledUpdates: make(map[cube.Pos]int64), + scheduledUpdates: make(map[cube.Pos]scheduledUpdate), entities: make(map[Entity]ChunkPos), viewers: make(map[*Loader]Viewer), chunks: make(map[ChunkPos]*chunkData), diff --git a/server/world/provider.go b/server/world/provider.go index 0353ff3d4..0e713ec1d 100644 --- a/server/world/provider.go +++ b/server/world/provider.go @@ -42,6 +42,12 @@ type Provider interface { // SaveBlockNBT saves block NBT, or block entities, to a specific chunk position. If the NBT cannot be // stored, SaveBlockNBT returns a non-nil error. SaveBlockNBT(position ChunkPos, data []map[string]any, dim Dimension) error + //// LoadScheduledUpdates loads the scheduled block updates for a chunk. If the updates cannot be loaded, + //// LoadScheduledUpdates returns a non-nil error. + //LoadScheduledUpdates(position ChunkPos, dim Dimension) ([]ScheduledUpdate, error) + //// SaveScheduledUpdates saves the scheduled block updates for a chunk. If the updates cannot be stored, + //// SaveScheduledUpdates returns a non-nil error. + //SaveScheduledUpdates(position ChunkPos, updates []ScheduledUpdate, dim Dimension) error } // Compile time check to make sure NopProvider implements Provider. @@ -68,8 +74,14 @@ func (NopProvider) LoadEntities(ChunkPos, Dimension, EntityRegistry) ([]Entity, func (NopProvider) SaveEntities(ChunkPos, []Entity, Dimension) error { return nil } func (NopProvider) LoadBlockNBT(ChunkPos, Dimension) ([]map[string]any, error) { return nil, nil } func (NopProvider) SaveBlockNBT(ChunkPos, []map[string]any, Dimension) error { return nil } -func (NopProvider) SaveChunk(ChunkPos, *chunk.Chunk, Dimension) error { return nil } -func (NopProvider) LoadChunk(ChunkPos, Dimension) (*chunk.Chunk, bool, error) { return nil, false, nil } + +// func (NopProvider) LoadScheduledUpdates(ChunkPos, Dimension) ([]ScheduledUpdate, error) { +// return nil, nil +// } +// +// func (NopProvider) SaveScheduledUpdates(ChunkPos, []ScheduledUpdate, Dimension) error { return nil } +func (NopProvider) SaveChunk(ChunkPos, *chunk.Chunk, Dimension) error { return nil } +func (NopProvider) LoadChunk(ChunkPos, Dimension) (*chunk.Chunk, bool, error) { return nil, false, nil } func (NopProvider) LoadPlayerSpawnPosition(uuid.UUID) (cube.Pos, bool, error) { return cube.Pos{}, false, nil } diff --git a/server/world/tick.go b/server/world/tick.go index cc866d01e..d0783c800 100644 --- a/server/world/tick.go +++ b/server/world/tick.go @@ -77,29 +77,34 @@ func (t ticker) tick() { // tickScheduledBlocks executes scheduled block updates in chunks that are currently loaded. func (t ticker) tickScheduledBlocks(tick int64) { t.w.updateMu.Lock() - positions := make([]cube.Pos, 0, len(t.w.scheduledUpdates)/4) - for pos, scheduledTick := range t.w.scheduledUpdates { - if scheduledTick <= tick { - positions = append(positions, pos) + updates := make([]scheduledUpdate, 0, len(t.w.scheduledUpdates)/4) + for _, update := range t.w.scheduledUpdates { + if update.t <= tick { + updates = append(updates, update) } } t.w.updateMu.Unlock() - for _, pos := range positions { - // We need to incrementally delete each scheduled update from the map, as each block update itself might attempt - // to schedule another block update, which could conflict with the current update selection. - // TODO: Definitely shouldn't lock like this. I need coffee. And sleep. - t.w.updateMu.Lock() - delete(t.w.scheduledUpdates, pos) - t.w.updateMu.Unlock() + for _, update := range updates { + targetHash := update.b.Hash() - if ticker, ok := t.w.Block(pos).(ScheduledTicker); ok { - ticker.ScheduledTick(pos, t.w, t.w.r) - } - if liquid, ok := t.w.additionalLiquid(pos); ok { - if ticker, ok := liquid.(ScheduledTicker); ok { - ticker.ScheduledTick(pos, t.w, t.w.r) + mainBlock := t.w.Block(update.p) + additionalBlock, _ := t.w.additionalLiquid(update.p) + for _, b := range []Block{mainBlock, additionalBlock} { + if b == nil { + continue + } + if b.Hash() != targetHash { + continue } + s, ok := b.(ScheduledTicker) + if !ok { + continue + } + + delete(t.w.scheduledUpdates, update.p) + + s.ScheduledTick(update.p, t.w, t.w.r) } } } diff --git a/server/world/world.go b/server/world/world.go index f3c5d0b91..f4551d11e 100644 --- a/server/world/world.go +++ b/server/world/world.go @@ -59,7 +59,7 @@ type World struct { // scheduledUpdates is a map of tick time values indexed by the block position at which an update is // scheduled. If the current tick exceeds the tick value passed, the block update will be performed // and the entry will be removed from the map. - scheduledUpdates map[cube.Pos]int64 + scheduledUpdates map[cube.Pos]scheduledUpdate neighbourUpdates []neighbourUpdate viewersMu sync.Mutex @@ -922,7 +922,7 @@ func (w *World) SetDifficulty(d Difficulty) { // ScheduleBlockUpdate schedules a block update at the position passed after a specific delay. If the block at // that position does not handle block updates, nothing will happen. -func (w *World) ScheduleBlockUpdate(pos cube.Pos, delay time.Duration) { +func (w *World) ScheduleBlockUpdate(b Block, pos cube.Pos, delay time.Duration) { if w == nil || pos.OutOfBounds(w.Range()) { return } @@ -935,7 +935,11 @@ func (w *World) ScheduleBlockUpdate(pos cube.Pos, delay time.Duration) { t := w.set.CurrentTick w.set.Unlock() - w.scheduledUpdates[pos] = t + delay.Nanoseconds()/int64(time.Second/20) + w.scheduledUpdates[pos] = scheduledUpdate{ + b: b, + p: pos, + t: t + (delay.Milliseconds() / 50), + } } // doBlockUpdatesAround schedules block updates directly around and on the position passed. @@ -1388,6 +1392,10 @@ func (w *World) saveChunk(pos ChunkPos, c *chunkData) { if err := w.provider().SaveEntities(pos, s, w.conf.Dim); err != nil { w.conf.Log.Errorf("error saving entities in chunk %v to provider: %v", pos, err) } + + //if err := w.provider().SaveScheduledUpdates(pos, maps.Values(c.s), w.conf.Dim); err != nil { + // w.conf.Log.Errorf("error saving scheduled updates in chunk %v to provider: %v", pos, err) + //} } ent := c.entities c.entities = nil @@ -1445,6 +1453,13 @@ type chunkData struct { entities []Entity } +// scheduledUpdate represents a scheduled update of a block. +type scheduledUpdate struct { + t int64 + b Block + p cube.Pos +} + // BlockEntities returns the block entities of the chunk. func (c *chunkData) BlockEntities() map[cube.Pos]Block { c.Lock() From feee4f0676f3ad7f53912ce6d0f2498ccc403e00 Mon Sep 17 00:00:00 2001 From: JustTal Date: Thu, 2 Feb 2023 20:38:22 -0600 Subject: [PATCH 67/83] block/piston_resolver.go: Glazed terracotta can't be pulled. --- server/block/piston_resolver.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/server/block/piston_resolver.go b/server/block/piston_resolver.go index 4ea960584..79cea6cd2 100644 --- a/server/block/piston_resolver.go +++ b/server/block/piston_resolver.go @@ -99,6 +99,11 @@ func (r *pistonResolver) calculateBlocks(pos cube.Pos, face cube.Face, breakFace return true } + if _, ok := block.(GlazedTerracotta); ok && face != breakFace { + // Glazed terracotta can't be pushed, but can be pulled. + return true + } + r.attachedPositions = append(r.attachedPositions, pos) if len(r.attachedPositions) >= 13 { r.breakPositions = nil From 5306c25fc467f3d551d7d58ef9990d3c516b6d44 Mon Sep 17 00:00:00 2001 From: JustTal Date: Thu, 2 Feb 2023 20:39:29 -0600 Subject: [PATCH 68/83] world: Remove commented code. --- server/world/provider.go | 16 ++-------------- server/world/world.go | 4 ---- 2 files changed, 2 insertions(+), 18 deletions(-) diff --git a/server/world/provider.go b/server/world/provider.go index 0e713ec1d..0353ff3d4 100644 --- a/server/world/provider.go +++ b/server/world/provider.go @@ -42,12 +42,6 @@ type Provider interface { // SaveBlockNBT saves block NBT, or block entities, to a specific chunk position. If the NBT cannot be // stored, SaveBlockNBT returns a non-nil error. SaveBlockNBT(position ChunkPos, data []map[string]any, dim Dimension) error - //// LoadScheduledUpdates loads the scheduled block updates for a chunk. If the updates cannot be loaded, - //// LoadScheduledUpdates returns a non-nil error. - //LoadScheduledUpdates(position ChunkPos, dim Dimension) ([]ScheduledUpdate, error) - //// SaveScheduledUpdates saves the scheduled block updates for a chunk. If the updates cannot be stored, - //// SaveScheduledUpdates returns a non-nil error. - //SaveScheduledUpdates(position ChunkPos, updates []ScheduledUpdate, dim Dimension) error } // Compile time check to make sure NopProvider implements Provider. @@ -74,14 +68,8 @@ func (NopProvider) LoadEntities(ChunkPos, Dimension, EntityRegistry) ([]Entity, func (NopProvider) SaveEntities(ChunkPos, []Entity, Dimension) error { return nil } func (NopProvider) LoadBlockNBT(ChunkPos, Dimension) ([]map[string]any, error) { return nil, nil } func (NopProvider) SaveBlockNBT(ChunkPos, []map[string]any, Dimension) error { return nil } - -// func (NopProvider) LoadScheduledUpdates(ChunkPos, Dimension) ([]ScheduledUpdate, error) { -// return nil, nil -// } -// -// func (NopProvider) SaveScheduledUpdates(ChunkPos, []ScheduledUpdate, Dimension) error { return nil } -func (NopProvider) SaveChunk(ChunkPos, *chunk.Chunk, Dimension) error { return nil } -func (NopProvider) LoadChunk(ChunkPos, Dimension) (*chunk.Chunk, bool, error) { return nil, false, nil } +func (NopProvider) SaveChunk(ChunkPos, *chunk.Chunk, Dimension) error { return nil } +func (NopProvider) LoadChunk(ChunkPos, Dimension) (*chunk.Chunk, bool, error) { return nil, false, nil } func (NopProvider) LoadPlayerSpawnPosition(uuid.UUID) (cube.Pos, bool, error) { return cube.Pos{}, false, nil } diff --git a/server/world/world.go b/server/world/world.go index f4551d11e..5733da58e 100644 --- a/server/world/world.go +++ b/server/world/world.go @@ -1392,10 +1392,6 @@ func (w *World) saveChunk(pos ChunkPos, c *chunkData) { if err := w.provider().SaveEntities(pos, s, w.conf.Dim); err != nil { w.conf.Log.Errorf("error saving entities in chunk %v to provider: %v", pos, err) } - - //if err := w.provider().SaveScheduledUpdates(pos, maps.Values(c.s), w.conf.Dim); err != nil { - // w.conf.Log.Errorf("error saving scheduled updates in chunk %v to provider: %v", pos, err) - //} } ent := c.entities c.entities = nil From 5991930814ff395db4a786fbbfbf4ee81016da60 Mon Sep 17 00:00:00 2001 From: JustTal Date: Fri, 3 Feb 2023 16:25:46 -0600 Subject: [PATCH 69/83] block/redstone.go: Implemented a RedstoneBlocking interface. --- server/block/observer.go | 9 +++++++-- server/block/redstone.go | 6 ++++++ server/world/block.go | 5 +++++ server/world/world.go | 3 +++ 4 files changed, 21 insertions(+), 2 deletions(-) diff --git a/server/block/observer.go b/server/block/observer.go index 26236908a..4420bf606 100644 --- a/server/block/observer.go +++ b/server/block/observer.go @@ -20,7 +20,12 @@ type Observer struct { } // Source ... -func (o Observer) Source() bool { +func (Observer) Source() bool { + return true +} + +// RedstoneBlocking ... +func (Observer) RedstoneBlocking() bool { return true } @@ -30,7 +35,7 @@ func (o Observer) WeakPower(pos cube.Pos, face cube.Face, w *world.World, accoun } // StrongPower ... -func (o Observer) StrongPower(_ cube.Pos, face cube.Face, w *world.World, _ bool) int { +func (o Observer) StrongPower(_ cube.Pos, face cube.Face, _ *world.World, _ bool) int { if !o.Powered || face != o.Facing { return 0 } diff --git a/server/block/redstone.go b/server/block/redstone.go index 3571c88bd..e04ea3ea9 100644 --- a/server/block/redstone.go +++ b/server/block/redstone.go @@ -13,6 +13,12 @@ type RedstoneUpdater interface { RedstoneUpdate(pos cube.Pos, w *world.World) } +// RedstoneBlocking represents a block that blocks redstone signals. +type RedstoneBlocking interface { + // RedstoneBlocking returns true if the block blocks redstone signals. + RedstoneBlocking() bool +} + // ComparatorEmitter represents a block that can emit a redstone signal through a comparator. type ComparatorEmitter interface { // ComparatorSignal returns the signal strength that is emitted through a comparator. diff --git a/server/world/block.go b/server/world/block.go index 851313b60..719530358 100644 --- a/server/world/block.go +++ b/server/world/block.go @@ -221,6 +221,11 @@ type lightDiffuser interface { LightDiffusionLevel() uint8 } +// redstoneBlocking is identical to a block.RedstoneBlocking. +type redstoneBlocking interface { + RedstoneBlocking() bool +} + // replaceableBlock represents a block that may be replaced by another block automatically. An example is // grass, which may be replaced by clicking it with another block. type replaceableBlock interface { diff --git a/server/world/world.go b/server/world/world.go index 5733da58e..238f8c511 100644 --- a/server/world/world.go +++ b/server/world/world.go @@ -1013,6 +1013,9 @@ func (w *World) RedstonePower(pos cube.Pos, face cube.Face, accountForDust bool) if c, ok := b.(Conductor); ok { power = c.WeakPower(pos, face, w, accountForDust) } + if b, ok := b.(redstoneBlocking); ok && b.RedstoneBlocking() { + return power + } if d, ok := b.(lightDiffuser); ok && d.LightDiffusionLevel() == 0 { return power } From a960f80ef9fd58d3cf098d8f2b4543c0bf524c08 Mon Sep 17 00:00:00 2001 From: JustTal Date: Fri, 3 Feb 2023 16:51:14 -0600 Subject: [PATCH 70/83] block/redstone.go: Implemented a PistonUpdater interface. --- server/block/observer.go | 5 +++++ server/block/piston.go | 10 ++++++++-- server/block/redstone.go | 6 ++++++ 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/server/block/observer.go b/server/block/observer.go index 4420bf606..00bd55f0d 100644 --- a/server/block/observer.go +++ b/server/block/observer.go @@ -63,6 +63,11 @@ func (o Observer) NeighbourUpdateTick(pos, changedNeighbour cube.Pos, w *world.W w.ScheduleBlockUpdate(o, pos, time.Millisecond*100) } +// PistonUpdate ... +func (o Observer) PistonUpdate(pos cube.Pos, w *world.World) { + w.ScheduleBlockUpdate(o, pos, time.Millisecond*100) +} + // UseOnBlock ... func (o Observer) UseOnBlock(pos cube.Pos, face cube.Face, _ mgl64.Vec3, w *world.World, user item.User, ctx *item.UseContext) bool { pos, _, used := firstReplaceable(w, pos, face, o) diff --git a/server/block/piston.go b/server/block/piston.go index 705244150..100c4e863 100644 --- a/server/block/piston.go +++ b/server/block/piston.go @@ -214,8 +214,11 @@ func (p Piston) push(pos cube.Pos, w *world.World) bool { } w.SetBlock(side, moving.Moving, nil) - if r, ok := moving.Moving.(RedstoneUpdater); ok { - r.RedstoneUpdate(side, w) + if u, ok := moving.Moving.(RedstoneUpdater); ok { + u.RedstoneUpdate(side, w) + } + if u, ok := moving.Moving.(PistonUpdater); ok { + u.PistonUpdate(side, w) } updateAroundRedstone(side, w) } @@ -292,6 +295,9 @@ func (p Piston) pull(pos cube.Pos, w *world.World) bool { if r, ok := moving.Moving.(RedstoneUpdater); ok { r.RedstoneUpdate(side, w) } + if r, ok := moving.Moving.(PistonUpdater); ok { + r.PistonUpdate(side, w) + } updateAroundRedstone(side, w) } diff --git a/server/block/redstone.go b/server/block/redstone.go index e04ea3ea9..455055d73 100644 --- a/server/block/redstone.go +++ b/server/block/redstone.go @@ -37,6 +37,12 @@ type PistonBreakable interface { PistonBreakable() bool } +// PistonUpdater represents a block that can be updated through a piston movement. +type PistonUpdater interface { + // PistonUpdate is called when a piston moves the block. + PistonUpdate(pos cube.Pos, w *world.World) +} + // wireNetwork implements a minimally-invasive bolt-on accelerator that performs a breadth-first search through redstone // wires in order to more efficiently and compute new redstone wire power levels and determine the order in which other // blocks should be updated. This implementation is heavily based off of RedstoneWireTurbo and MCHPRS. From 9dd470db63f07b371044d1d976742bab535c2be6 Mon Sep 17 00:00:00 2001 From: JustTal Date: Fri, 3 Feb 2023 17:06:46 -0600 Subject: [PATCH 71/83] world/world.go: Reverted scheduled update changes. (this caused too many issues than I anticipated!) --- server/block/button.go | 2 +- server/block/composter.go | 2 +- server/block/coral.go | 2 +- server/block/coral_block.go | 2 +- server/block/fire.go | 4 ++-- server/block/lava.go | 2 +- server/block/observer.go | 6 ++--- server/block/piston.go | 4 ++-- server/block/redstone_lamp.go | 2 +- server/block/redstone_repeater.go | 4 ++-- server/block/redstone_torch.go | 2 +- server/block/water.go | 2 +- server/item/fire_charge.go | 2 +- server/item/flint_and_steel.go | 2 +- server/world/conf.go | 2 +- server/world/tick.go | 39 ++++++++++++++----------------- server/world/world.go | 17 +++----------- 17 files changed, 40 insertions(+), 56 deletions(-) diff --git a/server/block/button.go b/server/block/button.go index c5c04dab0..ee38fc8fd 100644 --- a/server/block/button.go +++ b/server/block/button.go @@ -89,7 +89,7 @@ func (b Button) Activate(pos cube.Pos, _ cube.Face, w *world.World, u item.User, if b.Type == StoneButton() || b.Type == PolishedBlackstoneButton() { delay = time.Millisecond * 1000 } - w.ScheduleBlockUpdate(b, pos, delay) + w.ScheduleBlockUpdate(pos, delay) return true } diff --git a/server/block/composter.go b/server/block/composter.go index cbe3ce28f..a309a38d0 100644 --- a/server/block/composter.go +++ b/server/block/composter.go @@ -77,7 +77,7 @@ func (c Composter) Activate(pos cube.Pos, _ cube.Face, w *world.World, u item.Us w.SetBlock(pos, c, nil) w.PlaySound(pos.Vec3(), sound.ComposterFillLayer{}) if c.Level == 7 { - w.ScheduleBlockUpdate(c, pos, time.Second) + w.ScheduleBlockUpdate(pos, time.Second) } return true } diff --git a/server/block/coral.go b/server/block/coral.go index 652f9d884..fb139a0d8 100644 --- a/server/block/coral.go +++ b/server/block/coral.go @@ -64,7 +64,7 @@ func (c Coral) NeighbourUpdateTick(pos, _ cube.Pos, w *world.World) { if c.Dead { return } - w.ScheduleBlockUpdate(c, pos, time.Second*5/2) + w.ScheduleBlockUpdate(pos, time.Second*5/2) } // ScheduledTick ... diff --git a/server/block/coral_block.go b/server/block/coral_block.go index 42239639a..f351b0c32 100644 --- a/server/block/coral_block.go +++ b/server/block/coral_block.go @@ -23,7 +23,7 @@ func (c CoralBlock) NeighbourUpdateTick(pos, _ cube.Pos, w *world.World) { if c.Dead { return } - w.ScheduleBlockUpdate(c, pos, time.Second*5/2) + w.ScheduleBlockUpdate(pos, time.Second*5/2) } // ScheduledTick ... diff --git a/server/block/fire.go b/server/block/fire.go index bffdbb109..2f986d585 100644 --- a/server/block/fire.go +++ b/server/block/fire.go @@ -105,7 +105,7 @@ func (f Fire) tick(pos cube.Pos, w *world.World, r *rand.Rand) { w.SetBlock(pos, f, nil) } - w.ScheduleBlockUpdate(f, pos, time.Duration(30+r.Intn(10))*time.Second/20) + w.ScheduleBlockUpdate(pos, time.Duration(30+r.Intn(10))*time.Second/20) if !infinitelyBurns { _, waterBelow := w.Block(pos.Side(cube.FaceDown)).(Water) @@ -193,7 +193,7 @@ func (f Fire) spread(from, to cube.Pos, w *world.World, r *rand.Rand) { return } w.SetBlock(to, Fire{Type: f.Type, Age: min(15, f.Age+r.Intn(5)/4)}, nil) - w.ScheduleBlockUpdate(f, to, time.Duration(30+r.Intn(10))*time.Second/20) + w.ScheduleBlockUpdate(to, time.Duration(30+r.Intn(10))*time.Second/20) } // EntityInside ... diff --git a/server/block/lava.go b/server/block/lava.go index eb86a03b8..1610519c6 100644 --- a/server/block/lava.go +++ b/server/block/lava.go @@ -95,7 +95,7 @@ func (Lava) PistonBreakable() bool { // NeighbourUpdateTick ... func (l Lava) NeighbourUpdateTick(pos, _ cube.Pos, w *world.World) { if !l.Harden(pos, w, nil) { - w.ScheduleBlockUpdate(l, pos, w.Dimension().LavaSpreadDuration()) + w.ScheduleBlockUpdate(pos, w.Dimension().LavaSpreadDuration()) } } diff --git a/server/block/observer.go b/server/block/observer.go index 00bd55f0d..d487185e3 100644 --- a/server/block/observer.go +++ b/server/block/observer.go @@ -46,7 +46,7 @@ func (o Observer) StrongPower(_ cube.Pos, face cube.Face, _ *world.World, _ bool func (o Observer) ScheduledTick(pos cube.Pos, w *world.World, _ *rand.Rand) { o.Powered = !o.Powered if o.Powered { - w.ScheduleBlockUpdate(o, pos, time.Millisecond*100) + w.ScheduleBlockUpdate(pos, time.Millisecond*100) } w.SetBlock(pos, o, nil) updateDirectionalRedstone(pos, w, o.Facing.Opposite()) @@ -60,12 +60,12 @@ func (o Observer) NeighbourUpdateTick(pos, changedNeighbour cube.Pos, w *world.W if o.Powered { return } - w.ScheduleBlockUpdate(o, pos, time.Millisecond*100) + w.ScheduleBlockUpdate(pos, time.Millisecond*100) } // PistonUpdate ... func (o Observer) PistonUpdate(pos cube.Pos, w *world.World) { - w.ScheduleBlockUpdate(o, pos, time.Millisecond*100) + w.ScheduleBlockUpdate(pos, time.Millisecond*100) } // UseOnBlock ... diff --git a/server/block/piston.go b/server/block/piston.go index 100c4e863..e2d8a171d 100644 --- a/server/block/piston.go +++ b/server/block/piston.go @@ -138,7 +138,7 @@ func (p Piston) DecodeNBT(m map[string]any) any { // RedstoneUpdate ... func (p Piston) RedstoneUpdate(pos cube.Pos, w *world.World) { - w.ScheduleBlockUpdate(p, pos, time.Millisecond*50) + w.ScheduleBlockUpdate(pos, time.Millisecond*50) } // ScheduledTick ... @@ -150,7 +150,7 @@ func (p Piston) ScheduledTick(pos cube.Pos, w *world.World, _ *rand.Rand) { } else if !p.pull(pos, w) { return } - w.ScheduleBlockUpdate(p, pos, time.Millisecond*50) + w.ScheduleBlockUpdate(pos, time.Millisecond*50) } // armFace ... diff --git a/server/block/redstone_lamp.go b/server/block/redstone_lamp.go index 6fd2a0bb4..dcf1c60d8 100644 --- a/server/block/redstone_lamp.go +++ b/server/block/redstone_lamp.go @@ -63,7 +63,7 @@ func (l RedstoneLamp) RedstoneUpdate(pos cube.Pos, w *world.World) { l.Lit = true w.SetBlock(pos, l, &world.SetOpts{DisableBlockUpdates: true}) } else { - w.ScheduleBlockUpdate(l, pos, time.Millisecond*200) + w.ScheduleBlockUpdate(pos, time.Millisecond*200) } } diff --git a/server/block/redstone_repeater.go b/server/block/redstone_repeater.go index 621b525b7..077439eba 100644 --- a/server/block/redstone_repeater.go +++ b/server/block/redstone_repeater.go @@ -101,7 +101,7 @@ func (r RedstoneRepeater) RedstoneUpdate(pos cube.Pos, w *world.World) { return } if r.inputStrength(pos, w) > 0 != r.Powered { - w.ScheduleBlockUpdate(r, pos, time.Duration(r.Delay+1)*time.Millisecond*100) + w.ScheduleBlockUpdate(pos, time.Duration(r.Delay+1)*time.Millisecond*100) } } @@ -117,7 +117,7 @@ func (r RedstoneRepeater) ScheduledTick(pos cube.Pos, w *world.World, _ *rand.Ra updateGateRedstone(pos, w, r.Facing.Face().Opposite()) if r.Powered && r.inputStrength(pos, w) <= 0 { - w.ScheduleBlockUpdate(r, pos, time.Duration(r.Delay+1)*time.Millisecond*100) + w.ScheduleBlockUpdate(pos, time.Duration(r.Delay+1)*time.Millisecond*100) } w.SetBlock(pos, r, nil) updateGateRedstone(pos, w, r.Facing.Face()) diff --git a/server/block/redstone_torch.go b/server/block/redstone_torch.go index 8a0ae5c75..c36a97387 100644 --- a/server/block/redstone_torch.go +++ b/server/block/redstone_torch.go @@ -92,7 +92,7 @@ func (t RedstoneTorch) RedstoneUpdate(pos cube.Pos, w *world.World) { if t.inputStrength(pos, w) > 0 != t.Lit { return } - w.ScheduleBlockUpdate(t, pos, time.Millisecond*100) + w.ScheduleBlockUpdate(pos, time.Millisecond*100) } // ScheduledTick ... diff --git a/server/block/water.go b/server/block/water.go index fb5748c70..b6aeee087 100644 --- a/server/block/water.go +++ b/server/block/water.go @@ -124,7 +124,7 @@ func (w Water) NeighbourUpdateTick(pos, _ cube.Pos, wo *world.World) { wo.SetLiquid(pos, nil) return } - wo.ScheduleBlockUpdate(w, pos, time.Second/4) + wo.ScheduleBlockUpdate(pos, time.Second/4) } // LiquidType ... diff --git a/server/item/fire_charge.go b/server/item/fire_charge.go index 3c583588c..ceffd7ace 100644 --- a/server/item/fire_charge.go +++ b/server/item/fire_charge.go @@ -28,7 +28,7 @@ func (f FireCharge) UseOnBlock(pos cube.Pos, face cube.Face, _ mgl64.Vec3, w *wo ctx.SubtractFromCount(1) w.PlaySound(s.Vec3Centre(), sound.FireCharge{}) w.SetBlock(s, fire(), nil) - w.ScheduleBlockUpdate(w.Block(s), s, time.Duration(30+rand.Intn(10))*time.Second/20) + w.ScheduleBlockUpdate(s, time.Duration(30+rand.Intn(10))*time.Second/20) return true } return false diff --git a/server/item/flint_and_steel.go b/server/item/flint_and_steel.go index 3d1439913..2e7c19bfd 100644 --- a/server/item/flint_and_steel.go +++ b/server/item/flint_and_steel.go @@ -39,7 +39,7 @@ func (f FlintAndSteel) UseOnBlock(pos cube.Pos, face cube.Face, _ mgl64.Vec3, w } else if s := pos.Side(face); w.Block(s) == air() { w.PlaySound(s.Vec3Centre(), sound.Ignite{}) w.SetBlock(s, fire(), nil) - w.ScheduleBlockUpdate(w.Block(s), s, time.Duration(30+rand.Intn(10))*time.Second/20) + w.ScheduleBlockUpdate(s, time.Duration(30+rand.Intn(10))*time.Second/20) return true } return false diff --git a/server/world/conf.go b/server/world/conf.go index 4a3d54b72..a6b9ca905 100644 --- a/server/world/conf.go +++ b/server/world/conf.go @@ -71,7 +71,7 @@ func (conf Config) New() *World { } s := conf.Provider.Settings() w := &World{ - scheduledUpdates: make(map[cube.Pos]scheduledUpdate), + scheduledUpdates: make(map[cube.Pos]int64), entities: make(map[Entity]ChunkPos), viewers: make(map[*Loader]Viewer), chunks: make(map[ChunkPos]*chunkData), diff --git a/server/world/tick.go b/server/world/tick.go index d0783c800..cc866d01e 100644 --- a/server/world/tick.go +++ b/server/world/tick.go @@ -77,34 +77,29 @@ func (t ticker) tick() { // tickScheduledBlocks executes scheduled block updates in chunks that are currently loaded. func (t ticker) tickScheduledBlocks(tick int64) { t.w.updateMu.Lock() - updates := make([]scheduledUpdate, 0, len(t.w.scheduledUpdates)/4) - for _, update := range t.w.scheduledUpdates { - if update.t <= tick { - updates = append(updates, update) + positions := make([]cube.Pos, 0, len(t.w.scheduledUpdates)/4) + for pos, scheduledTick := range t.w.scheduledUpdates { + if scheduledTick <= tick { + positions = append(positions, pos) } } t.w.updateMu.Unlock() - for _, update := range updates { - targetHash := update.b.Hash() + for _, pos := range positions { + // We need to incrementally delete each scheduled update from the map, as each block update itself might attempt + // to schedule another block update, which could conflict with the current update selection. + // TODO: Definitely shouldn't lock like this. I need coffee. And sleep. + t.w.updateMu.Lock() + delete(t.w.scheduledUpdates, pos) + t.w.updateMu.Unlock() - mainBlock := t.w.Block(update.p) - additionalBlock, _ := t.w.additionalLiquid(update.p) - for _, b := range []Block{mainBlock, additionalBlock} { - if b == nil { - continue - } - if b.Hash() != targetHash { - continue - } - s, ok := b.(ScheduledTicker) - if !ok { - continue + if ticker, ok := t.w.Block(pos).(ScheduledTicker); ok { + ticker.ScheduledTick(pos, t.w, t.w.r) + } + if liquid, ok := t.w.additionalLiquid(pos); ok { + if ticker, ok := liquid.(ScheduledTicker); ok { + ticker.ScheduledTick(pos, t.w, t.w.r) } - - delete(t.w.scheduledUpdates, update.p) - - s.ScheduledTick(update.p, t.w, t.w.r) } } } diff --git a/server/world/world.go b/server/world/world.go index 238f8c511..605a7cd85 100644 --- a/server/world/world.go +++ b/server/world/world.go @@ -59,7 +59,7 @@ type World struct { // scheduledUpdates is a map of tick time values indexed by the block position at which an update is // scheduled. If the current tick exceeds the tick value passed, the block update will be performed // and the entry will be removed from the map. - scheduledUpdates map[cube.Pos]scheduledUpdate + scheduledUpdates map[cube.Pos]int64 neighbourUpdates []neighbourUpdate viewersMu sync.Mutex @@ -922,7 +922,7 @@ func (w *World) SetDifficulty(d Difficulty) { // ScheduleBlockUpdate schedules a block update at the position passed after a specific delay. If the block at // that position does not handle block updates, nothing will happen. -func (w *World) ScheduleBlockUpdate(b Block, pos cube.Pos, delay time.Duration) { +func (w *World) ScheduleBlockUpdate(pos cube.Pos, delay time.Duration) { if w == nil || pos.OutOfBounds(w.Range()) { return } @@ -935,11 +935,7 @@ func (w *World) ScheduleBlockUpdate(b Block, pos cube.Pos, delay time.Duration) t := w.set.CurrentTick w.set.Unlock() - w.scheduledUpdates[pos] = scheduledUpdate{ - b: b, - p: pos, - t: t + (delay.Milliseconds() / 50), - } + w.scheduledUpdates[pos] = t + (delay.Milliseconds() / 50) } // doBlockUpdatesAround schedules block updates directly around and on the position passed. @@ -1452,13 +1448,6 @@ type chunkData struct { entities []Entity } -// scheduledUpdate represents a scheduled update of a block. -type scheduledUpdate struct { - t int64 - b Block - p cube.Pos -} - // BlockEntities returns the block entities of the chunk. func (c *chunkData) BlockEntities() map[cube.Pos]Block { c.Lock() From 9905a6830419fb6fa3dcb0534535183b8cfb1c3e Mon Sep 17 00:00:00 2001 From: JustTal Date: Sat, 4 Feb 2023 00:43:23 -0600 Subject: [PATCH 72/83] block: Implemented droppers. --- server/block/dropper.go | 231 ++++++++++++++++++++++++++++++++++++ server/block/hash.go | 5 + server/block/redstone.go | 1 + server/block/register.go | 2 + server/session/player.go | 2 + server/session/world.go | 6 + server/world/sound/block.go | 6 + 7 files changed, 253 insertions(+) create mode 100644 server/block/dropper.go diff --git a/server/block/dropper.go b/server/block/dropper.go new file mode 100644 index 000000000..9ba9ff229 --- /dev/null +++ b/server/block/dropper.go @@ -0,0 +1,231 @@ +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" +) + +// Dropper is a low-capacity storage block that can eject its contents into the world or into other containers when +// given a redstone signal. +type Dropper struct { + solid + + // Facing is the direction the dropper is facing. + Facing cube.Face + // Powered is whether the dropper is powered or not. + Powered bool + // CustomName is the custom name of the dropper. This name is displayed when the dropper is opened, and may include + // colour codes. + CustomName string + + inventory *inventory.Inventory + viewerMu *sync.RWMutex + viewers map[ContainerViewer]struct{} +} + +// NewDropper creates a new initialised dropper. The inventory is properly initialised. +func NewDropper() Dropper { + m := new(sync.RWMutex) + v := make(map[ContainerViewer]struct{}, 1) + return Dropper{ + inventory: inventory.New(9, func(slot int, _, item item.Stack) { + m.RLock() + defer m.RUnlock() + for viewer := range v { + viewer.ViewSlotChange(slot, item) + } + }), + viewerMu: m, + viewers: v, + } +} + +// BreakInfo ... +func (d Dropper) BreakInfo() BreakInfo { + return newBreakInfo(3.5, pickaxeHarvestable, pickaxeEffective, oneOf(d)) +} + +// Inventory returns the inventory of the dropper. +func (d Dropper) Inventory() *inventory.Inventory { + return d.inventory +} + +// WithName returns the dropper after applying a specific name to the block. +func (d Dropper) WithName(a ...any) world.Item { + d.CustomName = strings.TrimSuffix(fmt.Sprintln(a...), "\n") + return d +} + +// AddViewer adds a viewer to the dropper, so that it is updated whenever the inventory of the dropper is changed. +func (d Dropper) AddViewer(v ContainerViewer, _ *world.World, _ cube.Pos) { + d.viewerMu.Lock() + defer d.viewerMu.Unlock() + d.viewers[v] = struct{}{} +} + +// RemoveViewer removes a viewer from the dropper, so that slot updates in the inventory are no longer sent to it. +func (d Dropper) RemoveViewer(v ContainerViewer, _ *world.World, _ cube.Pos) { + d.viewerMu.Lock() + defer d.viewerMu.Unlock() + delete(d.viewers, v) +} + +// Activate ... +func (Dropper) Activate(pos cube.Pos, _ cube.Face, _ *world.World, u item.User, _ *item.UseContext) bool { + if o, ok := u.(ContainerOpener); ok { + o.OpenBlockContainer(pos) + return true + } + return false +} + +// UseOnBlock ... +func (d Dropper) UseOnBlock(pos cube.Pos, face cube.Face, _ mgl64.Vec3, w *world.World, user item.User, ctx *item.UseContext) bool { + pos, _, used := firstReplaceable(w, pos, face, d) + if !used { + return false + } + //noinspection GoAssignmentToReceiver + d = NewDropper() + d.Facing = calculateAnySidedFace(user, pos, true) + + place(w, pos, d, user, ctx) + return placed(ctx) +} + +// RedstoneUpdate ... +func (d Dropper) RedstoneUpdate(pos cube.Pos, w *world.World) { + powered := receivedRedstonePower(pos, w) + if powered == d.Powered { + return + } + + d.Powered = powered + w.SetBlock(pos, d, nil) + if d.Powered { + w.ScheduleBlockUpdate(pos, time.Millisecond*200) + } +} + +// ScheduledTick ... +func (d Dropper) ScheduledTick(pos cube.Pos, w *world.World, r *rand.Rand) { + slot, ok := d.randomSlotFromInventory(r) + if !ok { + w.PlaySound(pos.Vec3Centre(), sound.DispenseFail{}) + return + } + + it, _ := d.Inventory().Item(slot) + if c, ok := w.Block(pos.Side(d.Facing)).(Container); ok { + if _, err := c.Inventory().AddItem(it.Grow(-it.Count() + 1)); err != nil { + return + } + _ = d.Inventory().SetItem(slot, it.Grow(-1)) + return + } + + _ = d.Inventory().SetItem(slot, it.Grow(-1)) + + n := r.Float64()/10 + 0.2 + + xOffset, zOffset := 0.0, 0.0 + if axis := d.Facing.Axis(); axis == cube.X { + xOffset = 1.0 + } else if axis == cube.Z { + zOffset = 1.0 + } + + xMultiplier, zMultiplier := -1.0, -1.0 + if d.Facing.Positive() { + xMultiplier, zMultiplier = 1.0, 1.0 + } + + w.PlaySound(pos.Vec3Centre(), sound.Dispense{}) + w.AddEntity(w.EntityRegistry().Config().Item( + it.Grow(-it.Count()+1), + pos.Vec3Centre().Add(cube.Pos{}.Side(d.Facing).Vec3().Mul(0.7)), + mgl64.Vec3{ + (r.Float64()*2-1)*6*0.0075 + xOffset*xMultiplier*n, + (r.Float64()*2-1)*6*0.0075 + 0.2, + (r.Float64()*2-1)*6*0.0075 + zOffset*zMultiplier*n, + }, + )) +} + +// randomSlotFromInventory returns a random slot from the inventory of the dropper. If the inventory is empty, the +// second return value is false. +func (d Dropper) randomSlotFromInventory(r *rand.Rand) (int, bool) { + slots := make([]int, 0, d.inventory.Size()) + for slot, it := range d.inventory.Slots() { + if !it.Empty() { + slots = append(slots, slot) + } + } + if len(slots) == 0 { + return 0, false + } + return slots[r.Intn(len(slots))], true +} + +// EncodeItem ... +func (Dropper) EncodeItem() (name string, meta int16) { + return "minecraft:dropper", 0 +} + +// EncodeBlock ... +func (d Dropper) EncodeBlock() (string, map[string]any) { + return "minecraft:dropper", map[string]any{ + "facing_direction": int32(d.Facing), + "triggered_bit": d.Powered, + } +} + +// EncodeNBT ... +func (d Dropper) EncodeNBT() map[string]any { + if d.inventory == nil { + facing, powered, customName := d.Facing, d.Powered, d.CustomName + //noinspection GoAssignmentToReceiver + d = NewDropper() + d.Facing, d.Powered, d.CustomName = facing, powered, customName + } + m := map[string]any{ + "Items": nbtconv.InvToNBT(d.inventory), + "id": "Dropper", + } + if d.CustomName != "" { + m["CustomName"] = d.CustomName + } + return m +} + +// DecodeNBT ... +func (d Dropper) DecodeNBT(data map[string]any) any { + facing, powered := d.Facing, d.Powered + //noinspection GoAssignmentToReceiver + d = NewDropper() + d.Facing = facing + d.Powered = powered + d.CustomName = nbtconv.String(data, "CustomName") + nbtconv.InvFromNBT(d.inventory, nbtconv.Slice(data, "Items")) + return d +} + +// allDroppers ... +func allDroppers() (droppers []world.Block) { + for _, f := range cube.Faces() { + for _, p := range []bool{false, true} { + droppers = append(droppers, Dropper{Facing: f, Powered: p}) + } + } + return droppers +} diff --git a/server/block/hash.go b/server/block/hash.go index 0277f49c2..7df1754d5 100644 --- a/server/block/hash.go +++ b/server/block/hash.go @@ -56,6 +56,7 @@ const ( hashDragonEgg hashDriedKelp hashDripstone + hashDropper hashEmerald hashEmeraldOre hashEnchantingTable @@ -395,6 +396,10 @@ func (Dripstone) Hash() uint64 { return hashDripstone } +func (d Dropper) Hash() uint64 { + return hashDropper | uint64(d.Facing)<<8 | uint64(boolByte(d.Powered))<<11 +} + func (Emerald) Hash() uint64 { return hashEmerald } diff --git a/server/block/redstone.go b/server/block/redstone.go index 455055d73..ea25fc392 100644 --- a/server/block/redstone.go +++ b/server/block/redstone.go @@ -135,6 +135,7 @@ func updateGateRedstone(centre cube.Pos, w *world.World, face cube.Face) { updateAroundRedstone(pos, w, face) } +// receivedRedstonePower returns true if the given position is receiving power from any faces that aren't ignored. func receivedRedstonePower(pos cube.Pos, w *world.World, ignoredFaces ...cube.Face) bool { for _, face := range cube.Faces() { if slices.Contains(ignoredFaces, face) { diff --git a/server/block/register.go b/server/block/register.go index 46a3df8ff..01aaea6e5 100644 --- a/server/block/register.go +++ b/server/block/register.go @@ -142,6 +142,7 @@ func init() { registerAll(allDoors()) registerAll(allDoubleFlowers()) registerAll(allDoubleTallGrass()) + registerAll(allDroppers()) registerAll(allEnderChests()) registerAll(allFarmland()) registerAll(allFence()) @@ -251,6 +252,7 @@ func init() { world.RegisterItem(DragonEgg{}) world.RegisterItem(DriedKelp{}) world.RegisterItem(Dripstone{}) + world.RegisterItem(Dropper{}) world.RegisterItem(Emerald{}) world.RegisterItem(EnchantingTable{}) world.RegisterItem(EndBricks{}) diff --git a/server/session/player.go b/server/session/player.go index d8ce8394b..8b512a8cb 100644 --- a/server/session/player.go +++ b/server/session/player.go @@ -153,6 +153,8 @@ func (s *Session) invByID(id int32) (*inventory.Inventory, bool) { return s.openedWindow.Load(), true } else if _, enderChest := b.(block.EnderChest); enderChest { return s.openedWindow.Load(), true + } else if _, dropper := b.(block.Dropper); dropper { + return s.openedWindow.Load(), true } } case protocol.ContainerBarrel: diff --git a/server/session/world.go b/server/session/world.go index 23fe61cee..e56b43886 100644 --- a/server/session/world.go +++ b/server/session/world.go @@ -755,6 +755,10 @@ func (s *Session) playSound(pos mgl64.Vec3, t world.Sound, disableRelative bool) pk.SoundType = packet.SoundEventPistonOut case sound.PistonRetract: pk.SoundType = packet.SoundEventPistonIn + case sound.DispenseFail: + pk.SoundType = packet.SoundEventBlockClickFail + case sound.Dispense: + pk.SoundType = packet.SoundEventBlockClick } s.writePacket(pk) } @@ -972,6 +976,8 @@ func (s *Session) openNormalContainer(b block.Container, pos cube.Pos) { containerType = protocol.ContainerTypeBlastFurnace case block.Smoker: containerType = protocol.ContainerTypeSmoker + case block.Dropper: + containerType = protocol.ContainerTypeDropper } s.writePacket(&packet.ContainerOpen{ diff --git a/server/world/sound/block.go b/server/world/sound/block.go index a276b3c73..153f8c79d 100644 --- a/server/world/sound/block.go +++ b/server/world/sound/block.go @@ -134,6 +134,12 @@ type PistonExtend struct{ sound } // PistonRetract is a sound played when a piston retracts. type PistonRetract struct{ sound } +// DispenseFail is a sound played when a dispenser fails to dispense an item. +type DispenseFail struct{ sound } + +// Dispense is a sound played when a dispenser dispenses an item. +type Dispense struct{ sound } + // sound implements the world.Sound interface. type sound struct{} From 4c08c329987a0b584b62db11dcc947a80e292985 Mon Sep 17 00:00:00 2001 From: JustTal Date: Sat, 4 Feb 2023 00:46:16 -0600 Subject: [PATCH 73/83] block/dropper.go: Simplified logic. --- server/block/dropper.go | 25 +++++-------------------- 1 file changed, 5 insertions(+), 20 deletions(-) diff --git a/server/block/dropper.go b/server/block/dropper.go index 9ba9ff229..e199d0cb7 100644 --- a/server/block/dropper.go +++ b/server/block/dropper.go @@ -119,22 +119,22 @@ func (d Dropper) RedstoneUpdate(pos cube.Pos, w *world.World) { // ScheduledTick ... func (d Dropper) ScheduledTick(pos cube.Pos, w *world.World, r *rand.Rand) { - slot, ok := d.randomSlotFromInventory(r) - if !ok { + items := d.inventory.Items() + if len(items) == 0 { w.PlaySound(pos.Vec3Centre(), sound.DispenseFail{}) return } - it, _ := d.Inventory().Item(slot) + it := items[r.Intn(len(items))] if c, ok := w.Block(pos.Side(d.Facing)).(Container); ok { if _, err := c.Inventory().AddItem(it.Grow(-it.Count() + 1)); err != nil { return } - _ = d.Inventory().SetItem(slot, it.Grow(-1)) + _ = d.Inventory().RemoveItem(it.Grow(-1)) return } - _ = d.Inventory().SetItem(slot, it.Grow(-1)) + _ = d.Inventory().RemoveItem(it.Grow(-1)) n := r.Float64()/10 + 0.2 @@ -162,21 +162,6 @@ func (d Dropper) ScheduledTick(pos cube.Pos, w *world.World, r *rand.Rand) { )) } -// randomSlotFromInventory returns a random slot from the inventory of the dropper. If the inventory is empty, the -// second return value is false. -func (d Dropper) randomSlotFromInventory(r *rand.Rand) (int, bool) { - slots := make([]int, 0, d.inventory.Size()) - for slot, it := range d.inventory.Slots() { - if !it.Empty() { - slots = append(slots, slot) - } - } - if len(slots) == 0 { - return 0, false - } - return slots[r.Intn(len(slots))], true -} - // EncodeItem ... func (Dropper) EncodeItem() (name string, meta int16) { return "minecraft:dropper", 0 From f6e1612b06e81b3c58f371d9dbfca3baf99efcb3 Mon Sep 17 00:00:00 2001 From: JustTal Date: Sat, 4 Feb 2023 00:50:35 -0600 Subject: [PATCH 74/83] block/dropper.go: Various fixes. --- server/block/dropper.go | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/server/block/dropper.go b/server/block/dropper.go index e199d0cb7..745b40171 100644 --- a/server/block/dropper.go +++ b/server/block/dropper.go @@ -125,16 +125,17 @@ func (d Dropper) ScheduledTick(pos cube.Pos, w *world.World, r *rand.Rand) { return } - it := items[r.Intn(len(items))] + sourceItem := items[r.Intn(len(items))] + targetItem := sourceItem.Grow(-sourceItem.Count() + 1) if c, ok := w.Block(pos.Side(d.Facing)).(Container); ok { - if _, err := c.Inventory().AddItem(it.Grow(-it.Count() + 1)); err != nil { + if _, err := c.Inventory().AddItem(targetItem); err != nil { return } - _ = d.Inventory().RemoveItem(it.Grow(-1)) + _ = d.Inventory().RemoveItem(targetItem) return } - _ = d.Inventory().RemoveItem(it.Grow(-1)) + _ = d.Inventory().RemoveItem(targetItem) n := r.Float64()/10 + 0.2 @@ -152,7 +153,7 @@ func (d Dropper) ScheduledTick(pos cube.Pos, w *world.World, r *rand.Rand) { w.PlaySound(pos.Vec3Centre(), sound.Dispense{}) w.AddEntity(w.EntityRegistry().Config().Item( - it.Grow(-it.Count()+1), + targetItem, pos.Vec3Centre().Add(cube.Pos{}.Side(d.Facing).Vec3().Mul(0.7)), mgl64.Vec3{ (r.Float64()*2-1)*6*0.0075 + xOffset*xMultiplier*n, From 6eae8f7904fbe7815ecefa00d2e8e08905442d4d Mon Sep 17 00:00:00 2001 From: JustTal Date: Sat, 4 Feb 2023 01:00:27 -0600 Subject: [PATCH 75/83] go.mod, go.sum: Updated dependencies. --- go.mod | 16 ++++++++-------- go.sum | 29 +++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 8 deletions(-) diff --git a/go.mod b/go.mod index 989a0eb16..88566144e 100644 --- a/go.mod +++ b/go.mod @@ -11,11 +11,11 @@ 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.26.0 - github.com/sirupsen/logrus v1.8.1 + github.com/sandertv/gophertunnel v1.26.1 + github.com/sirupsen/logrus v1.9.0 go.uber.org/atomic v1.9.0 - golang.org/x/exp v0.0.0-20220713135740-79cabaa25d75 - golang.org/x/text v0.3.7 + golang.org/x/exp v0.0.0-20230203172020-98cc5a0785f9 + golang.org/x/text v0.6.0 ) require ( @@ -26,12 +26,12 @@ require ( github.com/sandertv/go-raknet v1.12.0 // indirect github.com/stretchr/testify v1.8.1 // indirect golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 // indirect - golang.org/x/image v0.0.0-20220321031419-a8550c1d254a // indirect - golang.org/x/net v0.0.0-20220418201149-a630d4f3e7a2 // indirect + golang.org/x/image v0.3.0 // indirect + golang.org/x/net v0.5.0 // indirect golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5 // indirect - golang.org/x/sys v0.0.0-20220330033206-e17cdc41300f // indirect + golang.org/x/sys v0.4.0 // indirect google.golang.org/appengine v1.6.7 // indirect - google.golang.org/protobuf v1.28.0 // indirect + google.golang.org/protobuf v1.28.1 // indirect gopkg.in/square/go-jose.v2 v2.6.0 // indirect gopkg.in/yaml.v2 v2.3.0 // indirect ) diff --git a/go.sum b/go.sum index cc4482d05..dde55bc4f 100644 --- a/go.sum +++ b/go.sum @@ -149,8 +149,12 @@ github.com/sandertv/go-raknet v1.12.0 h1:olUzZlIJyX/pgj/mrsLCZYjKLNDsYiWdvQ4NIm3 github.com/sandertv/go-raknet v1.12.0/go.mod h1:Gx+WgZBMQ0V2UoouGoJ8Wj6CDrMBQ4SB2F/ggpl5/+Y= github.com/sandertv/gophertunnel v1.26.0 h1:6wCDkCh6j6t8wh0G+3U4X1KNVJtrmlj7yI58LuCh720= github.com/sandertv/gophertunnel v1.26.0/go.mod h1:dYFetA6r62huhc1EgR9p8VFAFtKOuGgVE/iXf5CzZ4o= +github.com/sandertv/gophertunnel v1.26.1 h1:5RAxqb++D5mMN2aZyuLZSFHvMKF0l77zvTsKSg+BS1M= +github.com/sandertv/gophertunnel v1.26.1/go.mod h1:dYFetA6r62huhc1EgR9p8VFAFtKOuGgVE/iXf5CzZ4o= github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= +github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72 h1:qLC7fQah7D6K1B0ujays3HV9gkFtllcxhzImRR7ArPQ= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -159,6 +163,7 @@ github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpE github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= @@ -166,6 +171,7 @@ github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= @@ -178,6 +184,7 @@ golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 h1:kUhD7nTDoI3fVd9G4ORWrbV5NY0liEs/Jg2pv5f+bBA= golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -192,11 +199,15 @@ golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EH golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= golang.org/x/exp v0.0.0-20220713135740-79cabaa25d75 h1:x03zeu7B2B11ySp+daztnwM5oBJ/8wGUSqrwcw9L0RA= golang.org/x/exp v0.0.0-20220713135740-79cabaa25d75/go.mod h1:Kr81I6Kryrl9sr8s2FK3vxD90NdsKWRuOIl2O4CvYbA= +golang.org/x/exp v0.0.0-20230203172020-98cc5a0785f9 h1:frX3nT9RkKybPnjyI+yvZh6ZucTZatCCEm9D47sZ2zo= +golang.org/x/exp v0.0.0-20230203172020-98cc5a0785f9/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190321063152-3fc05d484e9f/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/image v0.0.0-20220321031419-a8550c1d254a h1:LnH9RNcpPv5Kzi15lXg42lYMPUf0x8CuPv1YnvBWZAg= golang.org/x/image v0.0.0-20220321031419-a8550c1d254a/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= +golang.org/x/image v0.3.0 h1:HTDXbdK9bjfSWkPzDJIw89W8CAtfFGduujWs33NLLsg= +golang.org/x/image v0.3.0/go.mod h1:fXd9211C/0VTlYuAcOhW8dY/RtEJqODXOWBDpmYBf+A= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -215,6 +226,7 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -242,9 +254,13 @@ golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/ golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220418201149-a630d4f3e7a2 h1:6mzvA99KwZxbOrxww4EvWVQUnN1+xEu9tafK5ZxkYeA= golang.org/x/net v0.0.0-20220418201149-a630d4f3e7a2/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.5.0 h1:GyT4nK/YDHSqa1c4753ouYCDajOYKTja9Xb/OHtgvSw= +golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -260,6 +276,7 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -287,10 +304,17 @@ golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220330033206-e17cdc41300f h1:rlezHXNlxYWvBCzNses9Dlc7nGFaNMJeqLolcmQSSZY= golang.org/x/sys v0.0.0-20220330033206-e17cdc41300f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18= +golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -300,6 +324,8 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.6.0 h1:3XmdazWV+ubf7QgHSTWeykHOci5oeekaGJBLkrkaw4k= +golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -343,6 +369,7 @@ golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roY golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -426,6 +453,8 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0 google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= +google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= From 786d38c25b8dda71b2d1278a6761d8f05f0022ac Mon Sep 17 00:00:00 2001 From: DaPigGuy Date: Fri, 3 Feb 2023 23:00:56 -0800 Subject: [PATCH 76/83] block/jukebox.go: cleanup --- server/block/jukebox.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/server/block/jukebox.go b/server/block/jukebox.go index 2ee87f8b0..95f0dcc65 100644 --- a/server/block/jukebox.go +++ b/server/block/jukebox.go @@ -20,7 +20,7 @@ type Jukebox struct { } // Source ... -func (j Jukebox) Source() bool { +func (Jukebox) Source() bool { return true } @@ -33,12 +33,12 @@ func (j Jukebox) WeakPower(cube.Pos, cube.Face, *world.World, bool) int { } // StrongPower ... -func (j Jukebox) StrongPower(cube.Pos, cube.Face, *world.World, bool) int { +func (Jukebox) StrongPower(cube.Pos, cube.Face, *world.World, bool) int { return 0 } // FuelInfo ... -func (j Jukebox) FuelInfo() item.FuelInfo { +func (Jukebox) FuelInfo() item.FuelInfo { return newFuelInfo(time.Second * 15) } From 0ad69b37f7614071a337a673e04f7532308759f3 Mon Sep 17 00:00:00 2001 From: JustTal Date: Sat, 4 Feb 2023 10:49:43 -0600 Subject: [PATCH 77/83] block/dropper.go: Revert changes. We can't use RemoveItem without it always removing the first instance which technically isn't vanilla behaviour. --- server/block/dropper.go | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/server/block/dropper.go b/server/block/dropper.go index 745b40171..9ba9ff229 100644 --- a/server/block/dropper.go +++ b/server/block/dropper.go @@ -119,23 +119,22 @@ func (d Dropper) RedstoneUpdate(pos cube.Pos, w *world.World) { // ScheduledTick ... func (d Dropper) ScheduledTick(pos cube.Pos, w *world.World, r *rand.Rand) { - items := d.inventory.Items() - if len(items) == 0 { + slot, ok := d.randomSlotFromInventory(r) + if !ok { w.PlaySound(pos.Vec3Centre(), sound.DispenseFail{}) return } - sourceItem := items[r.Intn(len(items))] - targetItem := sourceItem.Grow(-sourceItem.Count() + 1) + it, _ := d.Inventory().Item(slot) if c, ok := w.Block(pos.Side(d.Facing)).(Container); ok { - if _, err := c.Inventory().AddItem(targetItem); err != nil { + if _, err := c.Inventory().AddItem(it.Grow(-it.Count() + 1)); err != nil { return } - _ = d.Inventory().RemoveItem(targetItem) + _ = d.Inventory().SetItem(slot, it.Grow(-1)) return } - _ = d.Inventory().RemoveItem(targetItem) + _ = d.Inventory().SetItem(slot, it.Grow(-1)) n := r.Float64()/10 + 0.2 @@ -153,7 +152,7 @@ func (d Dropper) ScheduledTick(pos cube.Pos, w *world.World, r *rand.Rand) { w.PlaySound(pos.Vec3Centre(), sound.Dispense{}) w.AddEntity(w.EntityRegistry().Config().Item( - targetItem, + it.Grow(-it.Count()+1), pos.Vec3Centre().Add(cube.Pos{}.Side(d.Facing).Vec3().Mul(0.7)), mgl64.Vec3{ (r.Float64()*2-1)*6*0.0075 + xOffset*xMultiplier*n, @@ -163,6 +162,21 @@ func (d Dropper) ScheduledTick(pos cube.Pos, w *world.World, r *rand.Rand) { )) } +// randomSlotFromInventory returns a random slot from the inventory of the dropper. If the inventory is empty, the +// second return value is false. +func (d Dropper) randomSlotFromInventory(r *rand.Rand) (int, bool) { + slots := make([]int, 0, d.inventory.Size()) + for slot, it := range d.inventory.Slots() { + if !it.Empty() { + slots = append(slots, slot) + } + } + if len(slots) == 0 { + return 0, false + } + return slots[r.Intn(len(slots))], true +} + // EncodeItem ... func (Dropper) EncodeItem() (name string, meta int16) { return "minecraft:dropper", 0 From 2186d15cf3b3e1a98e6c9c59ff74f365eeb4d5fe Mon Sep 17 00:00:00 2001 From: JustTal Date: Sat, 4 Feb 2023 11:26:01 -0600 Subject: [PATCH 78/83] block: Implemented (non-functional, for now) hoppers. --- server/block/hash.go | 5 ++ server/block/hopper.go | 166 +++++++++++++++++++++++++++++++++++++++ server/block/register.go | 2 + server/session/player.go | 2 + server/session/world.go | 2 + 5 files changed, 177 insertions(+) create mode 100644 server/block/hopper.go diff --git a/server/block/hash.go b/server/block/hash.go index 7df1754d5..f4b5cb429 100644 --- a/server/block/hash.go +++ b/server/block/hash.go @@ -81,6 +81,7 @@ const ( hashGrindstone hashHayBale hashHoneycomb + hashHopper hashInvisibleBedrock hashIron hashIronBars @@ -496,6 +497,10 @@ func (Honeycomb) Hash() uint64 { return hashHoneycomb } +func (h Hopper) Hash() uint64 { + return hashHopper | uint64(h.Facing)<<8 | uint64(boolByte(h.Powered))<<11 +} + func (InvisibleBedrock) Hash() uint64 { return hashInvisibleBedrock } diff --git a/server/block/hopper.go b/server/block/hopper.go new file mode 100644 index 000000000..8bbb53447 --- /dev/null +++ b/server/block/hopper.go @@ -0,0 +1,166 @@ +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/item/inventory" + "github.com/df-mc/dragonfly/server/world" + "github.com/go-gl/mathgl/mgl64" + "strings" + "sync" +) + +// Hopper is a low-capacity storage block that can be used to collect item entities directly above it, as well as to +// transfer items into and out of other containers. +// TODO: Functionality! +type Hopper struct { + transparent + + // Facing is the direction the hopper is facing. + Facing cube.Face + // Powered is whether the hopper is powered or not. + Powered bool + // CustomName is the custom name of the hopper. This name is displayed when the hopper is opened, and may include + // colour codes. + CustomName string + + inventory *inventory.Inventory + viewerMu *sync.RWMutex + viewers map[ContainerViewer]struct{} +} + +// NewHopper creates a new initialised hopper. The inventory is properly initialised. +func NewHopper() Hopper { + m := new(sync.RWMutex) + v := make(map[ContainerViewer]struct{}, 1) + return Hopper{ + inventory: inventory.New(5, func(slot int, _, item item.Stack) { + m.RLock() + defer m.RUnlock() + for viewer := range v { + viewer.ViewSlotChange(slot, item) + } + }), + viewerMu: m, + viewers: v, + } +} + +// Model ... +func (Hopper) Model() world.BlockModel { + // TODO: Implement me. + return model.Solid{} +} + +// BreakInfo ... +func (h Hopper) BreakInfo() BreakInfo { + return newBreakInfo(3, pickaxeHarvestable, pickaxeEffective, oneOf(h)) +} + +// Inventory returns the inventory of the hopper. +func (h Hopper) Inventory() *inventory.Inventory { + return h.inventory +} + +// WithName returns the hopper after applying a specific name to the block. +func (h Hopper) WithName(a ...any) world.Item { + h.CustomName = strings.TrimSuffix(fmt.Sprintln(a...), "\n") + return h +} + +// AddViewer adds a viewer to the hopper, so that it is updated whenever the inventory of the hopper is changed. +func (h Hopper) AddViewer(v ContainerViewer, _ *world.World, _ cube.Pos) { + h.viewerMu.Lock() + defer h.viewerMu.Unlock() + h.viewers[v] = struct{}{} +} + +// RemoveViewer removes a viewer from the hopper, so that slot updates in the inventory are no longer sent to it. +func (h Hopper) RemoveViewer(v ContainerViewer, _ *world.World, _ cube.Pos) { + h.viewerMu.Lock() + defer h.viewerMu.Unlock() + delete(h.viewers, v) +} + +// Activate ... +func (Hopper) Activate(pos cube.Pos, _ cube.Face, _ *world.World, u item.User, _ *item.UseContext) bool { + if o, ok := u.(ContainerOpener); ok { + o.OpenBlockContainer(pos) + return true + } + return false +} + +// UseOnBlock ... +func (h Hopper) UseOnBlock(pos cube.Pos, face cube.Face, _ mgl64.Vec3, w *world.World, user item.User, ctx *item.UseContext) bool { + pos, _, used := firstReplaceable(w, pos, face, h) + if !used { + return false + } + + //noinspection GoAssignmentToReceiver + h = NewHopper() + h.Facing = cube.FaceDown + if h.Facing != face { + h.Facing = face.Opposite() + } + + place(w, pos, h, user, ctx) + return placed(ctx) +} + +// EncodeItem ... +func (h Hopper) EncodeItem() (name string, meta int16) { + return "minecraft:hopper", 0 +} + +// EncodeBlock ... +func (h Hopper) EncodeBlock() (string, map[string]any) { + return "minecraft:hopper", map[string]any{ + "facing_direction": int32(h.Facing), + "toggle_bit": h.Powered, + } +} + +// EncodeNBT ... +func (h Hopper) EncodeNBT() map[string]any { + if h.inventory == nil { + facing, powered, customName := h.Facing, h.Powered, h.CustomName + //noinspection GoAssignmentToReceiver + h = NewHopper() + h.Facing, h.Powered, h.CustomName = facing, powered, customName + } + m := map[string]any{ + "Items": nbtconv.InvToNBT(h.inventory), + "id": "Hopper", + } + if h.CustomName != "" { + m["CustomName"] = h.CustomName + } + return m +} + +// DecodeNBT ... +func (h Hopper) DecodeNBT(data map[string]any) any { + facing, powered := h.Facing, h.Powered + //noinspection GoAssignmentToReceiver + h = NewHopper() + h.Facing = facing + h.Powered = powered + h.CustomName = nbtconv.String(data, "CustomName") + nbtconv.InvFromNBT(h.inventory, nbtconv.Slice(data, "Items")) + return h +} + +// allHoppers ... +func allHoppers() (hoppers []world.Block) { + for _, f := range cube.Faces() { + for _, p := range []bool{false, true} { + hoppers = append(hoppers, Hopper{Facing: f, Powered: p}) + } + } + return hoppers +} diff --git a/server/block/register.go b/server/block/register.go index 01aaea6e5..145782905 100644 --- a/server/block/register.go +++ b/server/block/register.go @@ -154,6 +154,7 @@ func init() { registerAll(allGlazedTerracotta()) registerAll(allGrindstones()) registerAll(allHayBales()) + registerAll(allHoppers()) registerAll(allItemFrames()) registerAll(allKelp()) registerAll(allLadders()) @@ -271,6 +272,7 @@ func init() { world.RegisterItem(Grindstone{}) world.RegisterItem(HayBale{}) world.RegisterItem(Honeycomb{}) + world.RegisterItem(Hopper{}) world.RegisterItem(InvisibleBedrock{}) world.RegisterItem(IronBars{}) world.RegisterItem(Iron{}) diff --git a/server/session/player.go b/server/session/player.go index 8b512a8cb..6b3016c56 100644 --- a/server/session/player.go +++ b/server/session/player.go @@ -155,6 +155,8 @@ func (s *Session) invByID(id int32) (*inventory.Inventory, bool) { return s.openedWindow.Load(), true } else if _, dropper := b.(block.Dropper); dropper { return s.openedWindow.Load(), true + } else if _, hopper := b.(block.Hopper); hopper { + return s.openedWindow.Load(), true } } case protocol.ContainerBarrel: diff --git a/server/session/world.go b/server/session/world.go index e56b43886..04ca4d531 100644 --- a/server/session/world.go +++ b/server/session/world.go @@ -978,6 +978,8 @@ func (s *Session) openNormalContainer(b block.Container, pos cube.Pos) { containerType = protocol.ContainerTypeSmoker case block.Dropper: containerType = protocol.ContainerTypeDropper + case block.Hopper: + containerType = protocol.ContainerTypeHopper } s.writePacket(&packet.ContainerOpen{ From 95a1254c40a7ceee6d15b448bbe6f1329d08e1c2 Mon Sep 17 00:00:00 2001 From: JustTal Date: Sun, 5 Feb 2023 09:29:09 -0600 Subject: [PATCH 79/83] block/dropper.go: Dispense particles. --- server/block/dropper.go | 13 ++++++++----- server/session/world.go | 5 +++++ server/world/particle/block.go | 3 +++ 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/server/block/dropper.go b/server/block/dropper.go index 9ba9ff229..294bcc0b8 100644 --- a/server/block/dropper.go +++ b/server/block/dropper.go @@ -7,6 +7,7 @@ import ( "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/particle" "github.com/df-mc/dragonfly/server/world/sound" "github.com/go-gl/mathgl/mgl64" "math/rand" @@ -136,7 +137,8 @@ func (d Dropper) ScheduledTick(pos cube.Pos, w *world.World, r *rand.Rand) { _ = d.Inventory().SetItem(slot, it.Grow(-1)) - n := r.Float64()/10 + 0.2 + dist := r.Float64()/10 + 0.2 + sourcePos := pos.Vec3Centre().Add(cube.Pos{}.Side(d.Facing).Vec3().Mul(0.7)) xOffset, zOffset := 0.0, 0.0 if axis := d.Facing.Axis(); axis == cube.X { @@ -150,14 +152,15 @@ func (d Dropper) ScheduledTick(pos cube.Pos, w *world.World, r *rand.Rand) { xMultiplier, zMultiplier = 1.0, 1.0 } - w.PlaySound(pos.Vec3Centre(), sound.Dispense{}) + w.PlaySound(sourcePos, sound.Dispense{}) + w.AddParticle(sourcePos, particle.Dispense{}) w.AddEntity(w.EntityRegistry().Config().Item( it.Grow(-it.Count()+1), - pos.Vec3Centre().Add(cube.Pos{}.Side(d.Facing).Vec3().Mul(0.7)), + sourcePos, mgl64.Vec3{ - (r.Float64()*2-1)*6*0.0075 + xOffset*xMultiplier*n, + (r.Float64()*2-1)*6*0.0075 + xOffset*xMultiplier*dist, (r.Float64()*2-1)*6*0.0075 + 0.2, - (r.Float64()*2-1)*6*0.0075 + zOffset*zMultiplier*n, + (r.Float64()*2-1)*6*0.0075 + zOffset*zMultiplier*dist, }, )) } diff --git a/server/session/world.go b/server/session/world.go index 04ca4d531..d0e4cef19 100644 --- a/server/session/world.go +++ b/server/session/world.go @@ -426,6 +426,11 @@ func (s *Session) ViewParticle(pos mgl64.Vec3, p world.Particle) { EventData: (int32(pa.Colour.A) << 24) | (int32(pa.Colour.R) << 16) | (int32(pa.Colour.G) << 8) | int32(pa.Colour.B), Position: vec64To32(pos), }) + case particle.Dispense: + s.writePacket(&packet.LevelEvent{ + EventType: packet.LevelEventParticlesShoot, + Position: vec64To32(pos), + }) case particle.Effect: s.writePacket(&packet.LevelEvent{ EventType: packet.LevelEventParticleLegacyEvent | 33, diff --git a/server/world/particle/block.go b/server/world/particle/block.go index 11748bf33..d01d2d7b4 100644 --- a/server/world/particle/block.go +++ b/server/world/particle/block.go @@ -81,6 +81,9 @@ type LavaDrip struct{ particle } // Lava is a particle that shows up randomly above lava. type Lava struct{ particle } +// Dispense is a particle that shows up when a dispenser or dropper dispenses an item. +type Dispense struct{ particle } + // particle serves as a base for all particles in this package. type particle struct{} From 37448dbe48e751a73a39dea07be9e1a1a4718579 Mon Sep 17 00:00:00 2001 From: JustTal Date: Sun, 5 Feb 2023 11:56:54 -0600 Subject: [PATCH 80/83] block/hopper.go: Item extraction. --- server/block/hopper.go | 92 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 89 insertions(+), 3 deletions(-) diff --git a/server/block/hopper.go b/server/block/hopper.go index 8bbb53447..22b0d79e3 100644 --- a/server/block/hopper.go +++ b/server/block/hopper.go @@ -27,6 +27,11 @@ type Hopper struct { // colour codes. CustomName string + // LastTick is the last world tick that the hopper was ticked. + LastTick int64 + // TransferCooldown is the duration until the hopper can transfer items again. + TransferCooldown int64 + inventory *inventory.Inventory viewerMu *sync.RWMutex viewers map[ContainerViewer]struct{} @@ -112,8 +117,87 @@ func (h Hopper) UseOnBlock(pos cube.Pos, face cube.Face, _ mgl64.Vec3, w *world. return placed(ctx) } +// Tick ... +func (h Hopper) Tick(currentTick int64, pos cube.Pos, w *world.World) { + h.TransferCooldown-- + h.LastTick = currentTick + if !h.Powered { + h.extractItemEntity(pos, w) + } + if h.TransferCooldown > 0 { + w.SetBlock(pos, h, nil) + return + } + + h.TransferCooldown = 0 + if h.Powered { + w.SetBlock(pos, h, nil) + return + } + + inserted := h.insertItem(pos, w) + extracted := h.extractItem(pos, w) + if inserted || extracted { + h.TransferCooldown = 8 + w.SetBlock(pos, h, nil) + } +} + +// insertItem ... +func (h Hopper) insertItem(pos cube.Pos, w *world.World) bool { + // TODO + return false +} + +// extractItem ... +func (h Hopper) extractItem(pos cube.Pos, w *world.World) bool { + origin, ok := w.Block(pos.Side(cube.FaceUp)).(Container) + if !ok { + return false + } + + var ( + targetSlot int + targetStack item.Stack + ) + switch o := origin.(type) { + case BlastFurnace, Furnace, Smoker: + fuel, _ := o.Inventory().Item(1) + if b, ok := fuel.Item().(item.Bucket); ok && b.Empty() { + targetStack, targetSlot = fuel, 1 + } else if output, _ := o.Inventory().Item(2); !output.Empty() { + targetStack, targetSlot = output, 2 + } + default: + for slot, stack := range origin.Inventory().Items() { + if stack.Empty() { + continue + } + targetStack, targetSlot = stack, slot + break + } + } + if targetStack.Empty() { + // We don't have any items to extract. + return false + } + + _, err := h.inventory.AddItem(targetStack.Grow(-targetStack.Count() + 1)) + if err != nil { + // The hopper is full. + return false + } + _ = origin.Inventory().SetItem(targetSlot, targetStack.Grow(-1)) + return true +} + +// extractItemEntity ... +func (h Hopper) extractItemEntity(pos cube.Pos, w *world.World) { + // TODO +} + // EncodeItem ... -func (h Hopper) EncodeItem() (name string, meta int16) { +func (Hopper) EncodeItem() (name string, meta int16) { return "minecraft:hopper", 0 } @@ -134,8 +218,9 @@ func (h Hopper) EncodeNBT() map[string]any { h.Facing, h.Powered, h.CustomName = facing, powered, customName } m := map[string]any{ - "Items": nbtconv.InvToNBT(h.inventory), - "id": "Hopper", + "Items": nbtconv.InvToNBT(h.inventory), + "TransferCooldown": int32(h.TransferCooldown), + "id": "Hopper", } if h.CustomName != "" { m["CustomName"] = h.CustomName @@ -151,6 +236,7 @@ func (h Hopper) DecodeNBT(data map[string]any) any { h.Facing = facing h.Powered = powered h.CustomName = nbtconv.String(data, "CustomName") + h.TransferCooldown = int64(nbtconv.Int32(data, "TransferCooldown")) nbtconv.InvFromNBT(h.inventory, nbtconv.Slice(data, "Items")) return h } From a5274d98268b506cf0d15437570bbf1a94abdaab Mon Sep 17 00:00:00 2001 From: JustTal Date: Sun, 5 Feb 2023 12:12:34 -0600 Subject: [PATCH 81/83] block/hopper.go: Abstracted custom extractables into their own interface. --- server/block/cake.go | 5 ----- server/block/hopper.go | 21 ++++++++++++--------- server/block/piston.go | 18 ++++++++++++++++++ server/block/redstone.go | 24 ------------------------ server/block/smelter.go | 11 +++++++++++ 5 files changed, 41 insertions(+), 38 deletions(-) diff --git a/server/block/cake.go b/server/block/cake.go index 0bedd23da..c1e880744 100644 --- a/server/block/cake.go +++ b/server/block/cake.go @@ -24,11 +24,6 @@ func (c Cake) SideClosed(cube.Pos, cube.Pos, *world.World) bool { return false } -// ComparatorSignal ... -func (c Cake) ComparatorSignal(cube.Pos, *world.World) int { - return (7 - c.Bites) * 2 -} - // UseOnBlock ... func (c Cake) UseOnBlock(pos cube.Pos, face cube.Face, _ mgl64.Vec3, w *world.World, user item.User, ctx *item.UseContext) bool { pos, _, used := firstReplaceable(w, pos, face, c) diff --git a/server/block/hopper.go b/server/block/hopper.go index 22b0d79e3..9df1865e9 100644 --- a/server/block/hopper.go +++ b/server/block/hopper.go @@ -149,6 +149,15 @@ func (h Hopper) insertItem(pos cube.Pos, w *world.World) bool { return false } +// HopperExtractable represents a block that can have its contents extracted by a hopper. +type HopperExtractable interface { + Container + + // ExtractItem attempts to extract a single item from the container. If the extraction was successful, the item is + // returned. If the extraction was unsuccessful, the item stack returned will be empty. + ExtractItem() (item.Stack, int) +} + // extractItem ... func (h Hopper) extractItem(pos cube.Pos, w *world.World) bool { origin, ok := w.Block(pos.Side(cube.FaceUp)).(Container) @@ -160,15 +169,7 @@ func (h Hopper) extractItem(pos cube.Pos, w *world.World) bool { targetSlot int targetStack item.Stack ) - switch o := origin.(type) { - case BlastFurnace, Furnace, Smoker: - fuel, _ := o.Inventory().Item(1) - if b, ok := fuel.Item().(item.Bucket); ok && b.Empty() { - targetStack, targetSlot = fuel, 1 - } else if output, _ := o.Inventory().Item(2); !output.Empty() { - targetStack, targetSlot = output, 2 - } - default: + if e, ok := origin.(HopperExtractable); !ok { for slot, stack := range origin.Inventory().Items() { if stack.Empty() { continue @@ -176,6 +177,8 @@ func (h Hopper) extractItem(pos cube.Pos, w *world.World) bool { targetStack, targetSlot = stack, slot break } + } else { + targetStack, targetSlot = e.ExtractItem() } if targetStack.Empty() { // We don't have any items to extract. diff --git a/server/block/piston.go b/server/block/piston.go index e2d8a171d..71ea8ca9a 100644 --- a/server/block/piston.go +++ b/server/block/piston.go @@ -38,6 +38,24 @@ type Piston struct { NewState int } +// PistonImmovable represents a block that cannot be moved by a piston. +type PistonImmovable interface { + // PistonImmovable returns whether the block is immovable. + PistonImmovable() bool +} + +// PistonBreakable represents a block that can be broken by a piston. +type PistonBreakable interface { + // PistonBreakable returns whether the block can be broken by a piston. + PistonBreakable() bool +} + +// PistonUpdater represents a block that can be updated through a piston movement. +type PistonUpdater interface { + // PistonUpdate is called when a piston moves the block. + PistonUpdate(pos cube.Pos, w *world.World) +} + // BreakInfo ... func (p Piston) BreakInfo() BreakInfo { return newBreakInfo(1.5, alwaysHarvestable, pickaxeEffective, oneOf(Piston{Sticky: p.Sticky})) diff --git a/server/block/redstone.go b/server/block/redstone.go index ea25fc392..48ca1121a 100644 --- a/server/block/redstone.go +++ b/server/block/redstone.go @@ -19,30 +19,6 @@ type RedstoneBlocking interface { RedstoneBlocking() bool } -// ComparatorEmitter represents a block that can emit a redstone signal through a comparator. -type ComparatorEmitter interface { - // ComparatorSignal returns the signal strength that is emitted through a comparator. - ComparatorSignal(pos cube.Pos, w *world.World) int -} - -// PistonImmovable represents a block that cannot be moved by a piston. -type PistonImmovable interface { - // PistonImmovable returns whether the block is immovable. - PistonImmovable() bool -} - -// PistonBreakable represents a block that can be broken by a piston. -type PistonBreakable interface { - // PistonBreakable returns whether the block can be broken by a piston. - PistonBreakable() bool -} - -// PistonUpdater represents a block that can be updated through a piston movement. -type PistonUpdater interface { - // PistonUpdate is called when a piston moves the block. - PistonUpdate(pos cube.Pos, w *world.World) -} - // wireNetwork implements a minimally-invasive bolt-on accelerator that performs a breadth-first search through redstone // wires in order to more efficiently and compute new redstone wire power levels and determine the order in which other // blocks should be updated. This implementation is heavily based off of RedstoneWireTurbo and MCHPRS. diff --git a/server/block/smelter.go b/server/block/smelter.go index 26b1a6fe2..7bf217647 100644 --- a/server/block/smelter.go +++ b/server/block/smelter.go @@ -85,6 +85,17 @@ func (s *smelter) RemoveViewer(v ContainerViewer, _ *world.World, _ cube.Pos) { delete(s.viewers, v) } +// ExtractItem ... +func (s *smelter) ExtractItem() (item.Stack, int) { + fuel, _ := s.Inventory().Item(1) + if b, ok := fuel.Item().(item.Bucket); ok && b.Empty() { + return fuel, 1 + } else if output, _ := s.Inventory().Item(2); !output.Empty() { + return output, 2 + } + return item.Stack{}, 0 +} + // setExperience sets the collected experience of the smelter to the given value. func (s *smelter) setExperience(xp int) { s.mu.Lock() From 05c4c09d97c6fbe5b87cbb486618437fdb84013e Mon Sep 17 00:00:00 2001 From: JustTal Date: Sun, 5 Feb 2023 12:47:15 -0600 Subject: [PATCH 82/83] block/hopper.go: Item entity extraction. --- server/block/hopper.go | 32 ++++++++++++++++++++++++++++++-- server/entity/item.go | 9 +++++++++ 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/server/block/hopper.go b/server/block/hopper.go index 9df1865e9..9bf322530 100644 --- a/server/block/hopper.go +++ b/server/block/hopper.go @@ -154,7 +154,8 @@ type HopperExtractable interface { Container // ExtractItem attempts to extract a single item from the container. If the extraction was successful, the item is - // returned. If the extraction was unsuccessful, the item stack returned will be empty. + // returned. If the extraction was unsuccessful, the item stack returned will be empty. ExtractItem by itself does + // should not remove the item from the container, but instead return the item that would be removed. ExtractItem() (item.Stack, int) } @@ -194,9 +195,36 @@ func (h Hopper) extractItem(pos cube.Pos, w *world.World) bool { return true } +// itemEntity ... +type itemEntity interface { + world.Entity + + Item() item.Stack + SetItem(item.Stack) +} + // extractItemEntity ... func (h Hopper) extractItemEntity(pos cube.Pos, w *world.World) { - // TODO + for _, e := range w.EntitiesWithin(cube.Box(0, 1, 0, 1, 2, 1).Translate(pos.Vec3()), func(entity world.Entity) bool { + _, ok := entity.(itemEntity) + return !ok + }) { + i := e.(itemEntity) + + stack := i.Item() + count, _ := h.inventory.AddItem(stack) + if count == 0 { + // We couldn't add any of the item to the inventory, so we continue to the next item entity. + continue + } + + if stack = stack.Grow(-count); stack.Empty() { + _ = i.Close() + return + } + i.SetItem(stack) + return + } } // EncodeItem ... diff --git a/server/entity/item.go b/server/entity/item.go index fd550385e..9d0af5751 100644 --- a/server/entity/item.go +++ b/server/entity/item.go @@ -46,9 +46,18 @@ func (it *Item) Type() world.EntityType { // Item returns the item stack that the item entity holds. func (it *Item) Item() item.Stack { + it.mu.Lock() + defer it.mu.Unlock() return it.i } +// SetItem sets the item stack that the item entity holds. +func (it *Item) SetItem(i item.Stack) { + it.mu.Lock() + defer it.mu.Unlock() + it.i = i +} + // SetPickupDelay sets a delay passed until the item can be picked up. If d is negative or d.Seconds()*20 // higher than math.MaxInt16, the item will never be able to be picked up. func (it *Item) SetPickupDelay(d time.Duration) { From a2ac369266c5cabc384baa4eaedbd32ffd92c526 Mon Sep 17 00:00:00 2001 From: JustTal Date: Sun, 5 Feb 2023 12:53:09 -0600 Subject: [PATCH 83/83] block/hopper.go: Waterlogging. --- server/block/hopper.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/server/block/hopper.go b/server/block/hopper.go index 9bf322530..7bb7b2ba9 100644 --- a/server/block/hopper.go +++ b/server/block/hopper.go @@ -18,6 +18,7 @@ import ( // TODO: Functionality! type Hopper struct { transparent + sourceWaterDisplacer // Facing is the direction the hopper is facing. Facing cube.Face @@ -60,6 +61,11 @@ func (Hopper) Model() world.BlockModel { return model.Solid{} } +// SideClosed ... +func (Hopper) SideClosed(cube.Pos, cube.Pos, *world.World) bool { + return false +} + // BreakInfo ... func (h Hopper) BreakInfo() BreakInfo { return newBreakInfo(3, pickaxeHarvestable, pickaxeEffective, oneOf(h))