2 Commits
Author SHA1 Message Date
adman 41d1ab4c45 performance 2026-03-24 17:46:23 +01:00
adman 032fa26927 fix some race conditions 2026-03-22 22:51:29 +01:00
6 changed files with 71 additions and 22 deletions
+8 -4
View File
@@ -53,6 +53,7 @@ type BlockState struct {
} }
var BlocksCache = make(map[string][]*Block) var BlocksCache = make(map[string][]*Block)
var BlocksCacheIdMap = make(map[string]map[int32]*Block)
func BlocksForVersion(v string) (ret []*Block) { func BlocksForVersion(v string) (ret []*Block) {
var ok bool var ok bool
@@ -66,13 +67,16 @@ func BlocksForVersion(v string) (ret []*Block) {
} }
func LookupBlockByStateId(version string, stateId int32) (block *Block, ok bool) { func LookupBlockByStateId(version string, stateId int32) (block *Block, ok bool) {
idmap, found := BlocksCacheIdMap[version]
if !found {
idmap = make(map[int32]*Block)
for _, b := range BlocksForVersion(version) { for _, b := range BlocksForVersion(version) {
if b.MinStateId <= stateId && stateId <= b.MaxStateId { for i := range b.MaxStateId - b.MinStateId + 1 {
block = b idmap[b.MinStateId+i] = b
ok = true
return
} }
} }
}
block, ok = idmap[stateId]
return return
} }
+8
View File
@@ -1,5 +1,7 @@
package data package data
import "sync"
// { // {
// "id": 2, // "id": 2,
// "name": "granite", // "name": "granite",
@@ -15,15 +17,21 @@ type Item struct {
} }
var ItemsCache = make(map[string][]*Item) var ItemsCache = make(map[string][]*Item)
var ItemsCacheMutex = &sync.RWMutex{}
func ItemsForVersion(v string) (ret []*Item) { func ItemsForVersion(v string) (ret []*Item) {
var ok bool var ok bool
ItemsCacheMutex.RLock()
if ret, ok = ItemsCache[v]; ok { if ret, ok = ItemsCache[v]; ok {
ItemsCacheMutex.RUnlock()
return return
} }
ItemsCacheMutex.RUnlock()
var b []*Item var b []*Item
must(LoadVersionedJson(v, "items", &b)) must(LoadVersionedJson(v, "items", &b))
ItemsCacheMutex.Lock()
ItemsCache[v] = b ItemsCache[v] = b
ItemsCacheMutex.Unlock()
return b return b
} }
+37 -7
View File
@@ -11,7 +11,7 @@ import (
) )
const ( const (
LoopsPerSecond = 100 // i dunno seems reasonable LoopsPerSecond = 10000 // this is just so the cpu doesn't burn itself in an unlimited infinite loop
TimePerLoop = time.Second / LoopsPerSecond TimePerLoop = time.Second / LoopsPerSecond
) )
@@ -35,16 +35,16 @@ type (
Tick struct{} Tick struct{}
) )
// HandlerDoneErr will stop the event loop and return the error if provided.
type HandlerDoneErr struct {
Return error
}
// Close is fired when the loop shuts down. // Close is fired when the loop shuts down.
type Close struct { type Close struct {
Reason error Reason error
} }
// HandlerDoneErr will stop the event loop and return the error if provided.
type HandlerDoneErr struct {
Return error
}
func (e HandlerDoneErr) Error() string { func (e HandlerDoneErr) Error() string {
return "HandlerDoneErr" return "HandlerDoneErr"
} }
@@ -53,12 +53,26 @@ func (e HandlerDoneErr) Unwrap() error {
return e.Return return e.Return
} }
// MainHandlerErr is a special error used to tell the event loop that the main loop has exited
type MainHandlerErr struct {
Return error
}
func (e MainHandlerErr) Error() string {
return "MainHandlerErr"
}
func (e MainHandlerErr) Unwrap() error {
return e.Return
}
// DontForwardErr will stop the event from being forwarded to other handlers, including anything handlers. // DontForwardErr will stop the event from being forwarded to other handlers, including anything handlers.
var DontForwardErr = errors.New("dont forward") var DontForwardErr = errors.New("dont forward")
var WhileClosingErr = errors.New("error while closing") var WhileClosingErr = errors.New("error while closing")
// CloseNonCriticalErr will close even when the event is non-critical // CloseNonCriticalErr will close even when the event is non-critical
var CloseNonCriticalErr = errors.New("close non-critical") var CloseNonCriticalErr = errors.New("close non-critical")
var ContextDoneErr = errors.New("context done")
func ValidateHandler(h any) (eventType reflect.Type, hv reflect.Value) { func ValidateHandler(h any) (eventType reflect.Type, hv reflect.Value) {
hv = reflect.ValueOf(h) hv = reflect.ValueOf(h)
@@ -146,6 +160,15 @@ func (l *Loop) Fire(event any) (err error) {
return return
} }
func (l *Loop) OnTick(_ Tick) (err error) {
select {
case <-l.Ctx.Done():
err = ContextDoneErr
default:
}
return
}
func (l *Loop) startLoop() (err error) { func (l *Loop) startLoop() (err error) {
_ = *l // exit early on nil loop _ = *l // exit early on nil loop
for { for {
@@ -168,10 +191,17 @@ func (l *Loop) startLoop() (err error) {
func (l *Loop) StartLoop() (err error) { func (l *Loop) StartLoop() (err error) {
go func() { go func() {
l.ErrChan <- l.startLoop() l.ErrChan <- MainHandlerErr{l.startLoop()}
}() }()
err = <-l.ErrChan err = <-l.ErrChan
l.cancel() l.cancel()
errMaybe := MainHandlerErr{}
if errors.As(err, &errMaybe) {
err = errors.Unwrap(err)
} else {
for !errors.Is(<-l.ErrChan, MainHandlerErr{}) {
}
}
return return
} }
+5 -5
View File
@@ -6,6 +6,7 @@ import (
"math" "math"
"os" "os"
"strings" "strings"
"unsafe"
"github.com/admin-else/strom/data" "github.com/admin-else/strom/data"
"github.com/admin-else/strom/level" "github.com/admin-else/strom/level"
@@ -42,10 +43,9 @@ type Region struct {
} }
func (r *Region) GetBlockAt(x, y, z int32) (name string, properties map[string]string, err error) { func (r *Region) GetBlockAt(x, y, z int32) (name string, properties map[string]string, err error) {
uintSlice := make([]uint64, len(r.BlockStates))
for i, v := range r.BlockStates { // this is probably fine
uintSlice[i] = uint64(v) uintSlice := unsafe.Slice((*uint64)(unsafe.Pointer(unsafe.SliceData(r.BlockStates))), len(r.BlockStates))
}
if r.Size.X < 0 { if r.Size.X < 0 {
r.Size.X *= -1 r.Size.X *= -1
} }
@@ -56,7 +56,7 @@ func (r *Region) GetBlockAt(x, y, z int32) (name string, properties map[string]s
r.Size.Z *= -1 r.Size.Z *= -1
} }
if x >= r.Size.X || y >= r.Size.Y || z >= r.Size.Z { if x >= r.Size.X || y >= r.Size.Y || z >= r.Size.Z || x < 0 || y < 0 || z < 0 {
err = OutOfBoundsErr err = OutOfBoundsErr
return return
} }
+3 -4
View File
@@ -2,6 +2,7 @@ package proto
import ( import (
"bytes" "bytes"
"context"
"errors" "errors"
"fmt" "fmt"
"io" "io"
@@ -19,7 +20,6 @@ var (
BadPacketTypeErr = errors.New("bad packet type") BadPacketTypeErr = errors.New("bad packet type")
BadPacketIdErr = errors.New("bad packet id") BadPacketIdErr = errors.New("bad packet id")
PacketNotFullyDecodedErr = errors.New("packet not fully decoded") PacketNotFullyDecodedErr = errors.New("packet not fully decoded")
ContextDoneErr = errors.New("context done")
WrongStateErr = errors.New("wrong state") WrongStateErr = errors.New("wrong state")
WrongDirectionErr = errors.New("wrong direction") WrongDirectionErr = errors.New("wrong direction")
CantTranslateErr = errors.New("cant translate packet") CantTranslateErr = errors.New("cant translate packet")
@@ -222,11 +222,10 @@ func (c *Conn) OnTick(_ event.Tick) (err error) {
if c.receiveRoutine { if c.receiveRoutine {
select { select {
case packet = <-c.packetCh: case packet = <-c.packetCh:
if slog.Default().Enabled(context.Background(), slog.LevelDebug) {
slog.Debug("receive", "actor", c.Actor, "packet", fmt.Sprintf("%#v", packet)) slog.Debug("receive", "actor", c.Actor, "packet", fmt.Sprintf("%#v", packet))
}
err = c.Loop.Fire(packet) err = c.Loop.Fire(packet)
case <-c.Ctx.Done():
err = ContextDoneErr
return
default: default:
} }
} else { } else {
+8
View File
@@ -0,0 +1,8 @@
package util
import "math/rand/v2"
func ShuffleSlice[T any](s []T) (ret []T) {
rand.Shuffle(len(s), func(i, j int) { s[i], s[j] = s[j], s[i] })
return s
}