133 lines
3.2 KiB
Go
133 lines
3.2 KiB
Go
|
|
// Package redstone 方块更新队列与红石信号传播(场景/红石与方块更新.md §2–§3)。
|
|||
|
|
//
|
|||
|
|
// 设计要点:
|
|||
|
|
// - 延迟更新队列(0/1/2/4 tick)+ 每 tick 迭代上限(防无限递归);
|
|||
|
|
// - 红石信号 0–15,仅沿线缆传播(每格衰减 1),与方块光完全分离。
|
|||
|
|
package redstone
|
|||
|
|
|
|||
|
|
import (
|
|||
|
|
"sync"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
// 更新类型(红石与方块更新.md §2)。
|
|||
|
|
const (
|
|||
|
|
KindNeighbor = 0 // 邻居变化
|
|||
|
|
KindScheduled = 1 // 定时刻(延迟更新)
|
|||
|
|
KindBlockEntity = 2 // 方块实体 tick
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
// 迭代上限(红石与方块更新.md §2:每 tick 20000,防级联风暴)。
|
|||
|
|
const DefaultIterLimit = 20000
|
|||
|
|
|
|||
|
|
// Update 一个方块更新事件。
|
|||
|
|
type Update struct {
|
|||
|
|
X, Y, Z int32
|
|||
|
|
Kind uint8
|
|||
|
|
Delay int // 剩余延迟 tick
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Queue 延迟更新队列(有序消费)。
|
|||
|
|
type Queue struct {
|
|||
|
|
mu sync.Mutex
|
|||
|
|
events []Update
|
|||
|
|
iterCap int
|
|||
|
|
dropped int // 超限丢弃计数(告警用)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// NewQueue 创建队列。
|
|||
|
|
func NewQueue() *Queue { return &Queue{iterCap: DefaultIterLimit} }
|
|||
|
|
|
|||
|
|
// Push 入队。
|
|||
|
|
func (q *Queue) Push(u Update) {
|
|||
|
|
q.mu.Lock()
|
|||
|
|
q.events = append(q.events, u)
|
|||
|
|
q.mu.Unlock()
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Pending 待处理事件数。
|
|||
|
|
func (q *Queue) Pending() int {
|
|||
|
|
q.mu.Lock()
|
|||
|
|
defer q.mu.Unlock()
|
|||
|
|
return len(q.events)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Dropped 返回被丢弃的事件数(迭代上限触发次数)。
|
|||
|
|
func (q *Queue) Dropped() int {
|
|||
|
|
q.mu.Lock()
|
|||
|
|
defer q.mu.Unlock()
|
|||
|
|
return q.dropped
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Tick 每 tick 消费队列:延迟递减,到期的事件交给 handle 处理。
|
|||
|
|
// 处理数量受 iterCap 限制;超限事件丢弃并计数(红石与方块更新.md §6 踩坑)。
|
|||
|
|
func (q *Queue) Tick(handle func(u Update)) {
|
|||
|
|
q.mu.Lock()
|
|||
|
|
kept := q.events[:0]
|
|||
|
|
var due []Update
|
|||
|
|
for _, u := range q.events {
|
|||
|
|
if u.Delay > 0 {
|
|||
|
|
u.Delay--
|
|||
|
|
kept = append(kept, u)
|
|||
|
|
continue
|
|||
|
|
}
|
|||
|
|
due = append(due, u)
|
|||
|
|
}
|
|||
|
|
q.events = kept
|
|||
|
|
q.mu.Unlock()
|
|||
|
|
|
|||
|
|
processed := 0
|
|||
|
|
for _, u := range due {
|
|||
|
|
if processed >= q.iterCap {
|
|||
|
|
q.mu.Lock()
|
|||
|
|
q.dropped++
|
|||
|
|
q.mu.Unlock()
|
|||
|
|
continue
|
|||
|
|
}
|
|||
|
|
handle(u)
|
|||
|
|
processed++
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 红石信号(红石与方块更新.md §3:0–15,与方块光分离)。
|
|||
|
|
|
|||
|
|
// Src 信号源。
|
|||
|
|
type Src struct {
|
|||
|
|
X, Y, Z int32
|
|||
|
|
Level uint8 // 0–15
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// SpreadWire 红石信号沿线缆传播:每格衰减 1,仅经过 isWire 判定的线缆格。
|
|||
|
|
// set 写信号值;返回值无(传播结果由调用方通过 set 收集)。
|
|||
|
|
// 与光照 Propagate 的关键区别:红石只走线缆、不穿过所有方块(红石与方块更新.md §6 踩坑)。
|
|||
|
|
func SpreadWire(sources []Src, isWire func(x, y, z int32) bool, set func(x, y, z int32, v uint8)) {
|
|||
|
|
type pos struct{ x, y, z int32 }
|
|||
|
|
q := make([]pos, 0, 64)
|
|||
|
|
power := make(map[pos]uint8, 64)
|
|||
|
|
for _, s := range sources {
|
|||
|
|
p := pos{s.X, s.Y, s.Z}
|
|||
|
|
power[p] = s.Level
|
|||
|
|
set(p.x, p.y, p.z, s.Level)
|
|||
|
|
q = append(q, p)
|
|||
|
|
}
|
|||
|
|
dirs := [6][3]int32{{1, 0, 0}, {-1, 0, 0}, {0, 1, 0}, {0, -1, 0}, {0, 0, 1}, {0, 0, -1}}
|
|||
|
|
for len(q) > 0 {
|
|||
|
|
p := q[0]
|
|||
|
|
q = q[1:]
|
|||
|
|
lv := power[p]
|
|||
|
|
if lv <= 1 {
|
|||
|
|
continue
|
|||
|
|
}
|
|||
|
|
for _, d := range dirs {
|
|||
|
|
n := pos{p.x + d[0], p.y + d[1], p.z + d[2]}
|
|||
|
|
if !isWire(n.x, n.y, n.z) {
|
|||
|
|
continue
|
|||
|
|
}
|
|||
|
|
if old, ok := power[n]; !ok || old < lv-1 {
|
|||
|
|
power[n] = lv - 1
|
|||
|
|
set(n.x, n.y, n.z, lv-1)
|
|||
|
|
q = append(q, n)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|