delete ecs system
This commit is contained in:
158
examples/dazeReadCommand/daze.go
Normal file
158
examples/dazeReadCommand/daze.go
Normal file
@ -0,0 +1,158 @@
|
|||||||
|
// Daze could join an offline-mode server as client.
|
||||||
|
// Just standing there and do nothing. Automatically reborn after five seconds of death.
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
//"github.com/mattn/go-colorable"
|
||||||
|
|
||||||
|
"github.com/Tnze/go-mc/bot"
|
||||||
|
"github.com/Tnze/go-mc/bot/basic"
|
||||||
|
"github.com/Tnze/go-mc/bot/screen"
|
||||||
|
_ "github.com/Tnze/go-mc/data/lang/zh-cn"
|
||||||
|
"github.com/Tnze/go-mc/data/packetid"
|
||||||
|
pk "github.com/Tnze/go-mc/net/packet"
|
||||||
|
)
|
||||||
|
|
||||||
|
var address = flag.String("address", "127.0.0.1", "The server address")
|
||||||
|
var client *bot.Client
|
||||||
|
var player *basic.Player
|
||||||
|
var screenManager *screen.Manager
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
flag.Parse()
|
||||||
|
//log.SetOutput(colorable.NewColorableStdout())
|
||||||
|
client = bot.NewClient()
|
||||||
|
client.Auth.Name = "Daze"
|
||||||
|
player = basic.NewPlayer(client, basic.DefaultSettings)
|
||||||
|
client.Events.AddListener(bot.PacketHandler{
|
||||||
|
ID: packetid.ClientboundCommands,
|
||||||
|
Priority: 50,
|
||||||
|
F: onCommands,
|
||||||
|
})
|
||||||
|
|
||||||
|
//Login
|
||||||
|
err := client.JoinServer(*address)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
log.Println("Login success")
|
||||||
|
|
||||||
|
//JoinGame
|
||||||
|
for {
|
||||||
|
if err = client.HandleGame(); err == nil {
|
||||||
|
panic("HandleGame never return nil")
|
||||||
|
}
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func onCommands(p pk.Packet) error {
|
||||||
|
var nodes []Node
|
||||||
|
var root pk.VarInt
|
||||||
|
err := p.Scan(pk.Array(&nodes), &root)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
log.Printf("Root index: %d", root)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type Node struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n Node) ReadFrom(r io.Reader) (int64, error) {
|
||||||
|
var Flags pk.Byte
|
||||||
|
var Children []pk.VarInt
|
||||||
|
var Redirect pk.VarInt
|
||||||
|
var Name pk.String
|
||||||
|
var Parser pk.Identifier
|
||||||
|
var Properties = Prop{Type: &Parser}
|
||||||
|
var SuggestionsType pk.Identifier
|
||||||
|
m, err := pk.Tuple{
|
||||||
|
&Flags,
|
||||||
|
pk.Array(&Children),
|
||||||
|
pk.Opt{
|
||||||
|
Has: func() bool { return Flags&0x08 != 0 },
|
||||||
|
Field: &Redirect,
|
||||||
|
},
|
||||||
|
pk.Opt{
|
||||||
|
Has: func() bool { return Flags&0x03 == 2 || Flags&0x03 == 1 },
|
||||||
|
Field: &Name,
|
||||||
|
},
|
||||||
|
pk.Opt{
|
||||||
|
Has: func() bool { return Flags&0x03 == 2 },
|
||||||
|
Field: pk.Tuple{&Parser, &Properties},
|
||||||
|
},
|
||||||
|
pk.Opt{
|
||||||
|
Has: func() bool { return Flags&0x10 != 0 },
|
||||||
|
Field: &SuggestionsType,
|
||||||
|
},
|
||||||
|
}.ReadFrom(r)
|
||||||
|
if err != nil {
|
||||||
|
return m, err
|
||||||
|
}
|
||||||
|
var redirect string
|
||||||
|
if Flags&0x08 != 0 {
|
||||||
|
redirect = "Redirect: " + strconv.Itoa(int(Redirect))
|
||||||
|
}
|
||||||
|
var parser string
|
||||||
|
if Flags&0x03 == 2 {
|
||||||
|
redirect = string("Parser: " + Parser)
|
||||||
|
}
|
||||||
|
log.Printf("Type: %2d\tName: %s\tChildren: %v\t%v\t%v", Flags&0x03, Name, Children, redirect, parser)
|
||||||
|
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type Prop struct {
|
||||||
|
Type *pk.Identifier
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p Prop) ReadFrom(r io.Reader) (int64, error) {
|
||||||
|
var Flags pk.Byte
|
||||||
|
switch *p.Type {
|
||||||
|
case "brigadier:double":
|
||||||
|
var Min, Max pk.Double
|
||||||
|
return pk.Tuple{
|
||||||
|
&Flags,
|
||||||
|
pk.Opt{Has: func() bool { return Flags&0x01 != 0 }, Field: &Min},
|
||||||
|
pk.Opt{Has: func() bool { return Flags&0x02 != 0 }, Field: &Max},
|
||||||
|
}.ReadFrom(r)
|
||||||
|
case "brigadier:float":
|
||||||
|
var Min, Max pk.Float
|
||||||
|
return pk.Tuple{
|
||||||
|
&Flags,
|
||||||
|
pk.Opt{Has: func() bool { return Flags&0x01 != 0 }, Field: &Min},
|
||||||
|
pk.Opt{Has: func() bool { return Flags&0x02 != 0 }, Field: &Max},
|
||||||
|
}.ReadFrom(r)
|
||||||
|
case "brigadier:integer":
|
||||||
|
var Min, Max pk.Int
|
||||||
|
return pk.Tuple{
|
||||||
|
&Flags,
|
||||||
|
pk.Opt{Has: func() bool { return Flags&0x01 != 0 }, Field: &Min},
|
||||||
|
pk.Opt{Has: func() bool { return Flags&0x02 != 0 }, Field: &Max},
|
||||||
|
}.ReadFrom(r)
|
||||||
|
case "brigadier:long":
|
||||||
|
var Min, Max pk.Long
|
||||||
|
return pk.Tuple{
|
||||||
|
&Flags,
|
||||||
|
pk.Opt{Has: func() bool { return Flags&0x01 != 0 }, Field: &Min},
|
||||||
|
pk.Opt{Has: func() bool { return Flags&0x02 != 0 }, Field: &Max},
|
||||||
|
}.ReadFrom(r)
|
||||||
|
case "brigadier:string":
|
||||||
|
return new(pk.VarInt).ReadFrom(r)
|
||||||
|
case "minecraft:entity":
|
||||||
|
return new(pk.Byte).ReadFrom(r)
|
||||||
|
case "minecraft:score_holder":
|
||||||
|
return new(pk.Byte).ReadFrom(r)
|
||||||
|
case "minecraft:range":
|
||||||
|
return new(pk.Boolean).ReadFrom(r)
|
||||||
|
default:
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
}
|
@ -4,8 +4,6 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
_ "embed"
|
_ "embed"
|
||||||
"flag"
|
"flag"
|
||||||
"github.com/Tnze/go-mc/server/ecs"
|
|
||||||
"github.com/Tnze/go-mc/server/world"
|
|
||||||
"image"
|
"image"
|
||||||
_ "image/png"
|
_ "image/png"
|
||||||
"log"
|
"log"
|
||||||
@ -14,7 +12,6 @@ import (
|
|||||||
"github.com/Tnze/go-mc/chat"
|
"github.com/Tnze/go-mc/chat"
|
||||||
"github.com/Tnze/go-mc/server"
|
"github.com/Tnze/go-mc/server"
|
||||||
"github.com/Tnze/go-mc/server/command"
|
"github.com/Tnze/go-mc/server/command"
|
||||||
"github.com/Tnze/go-mc/server/player"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var motd = chat.Message{Text: "A Minecraft Server ", Extra: []chat.Message{{Text: "Powered by go-mc", Color: "yellow"}}}
|
var motd = chat.Message{Text: "A Minecraft Server ", Extra: []chat.Message{{Text: "Powered by go-mc", Color: "yellow"}}}
|
||||||
@ -57,13 +54,6 @@ func main() {
|
|||||||
keepAlive,
|
keepAlive,
|
||||||
commands,
|
commands,
|
||||||
)
|
)
|
||||||
ecs.Register[world.Dimension, *ecs.HashMapStorage[world.Dimension]](game.World)
|
|
||||||
dimList := world.NewDimensionManager(game)
|
|
||||||
dimList.Add(game.CreateEntity(world.NewDimension(
|
|
||||||
"minecraft:overworld", *regionPath,
|
|
||||||
)), "minecraft:overworld")
|
|
||||||
player.SpawnSystem(game, *playerdataPath)
|
|
||||||
player.PosAndRotSystem(game)
|
|
||||||
go game.Run(context.Background())
|
go game.Run(context.Background())
|
||||||
|
|
||||||
s := server.Server{
|
s := server.Server{
|
||||||
|
@ -9,13 +9,11 @@ import (
|
|||||||
|
|
||||||
"github.com/Tnze/go-mc/net"
|
"github.com/Tnze/go-mc/net"
|
||||||
pk "github.com/Tnze/go-mc/net/packet"
|
pk "github.com/Tnze/go-mc/net/packet"
|
||||||
"github.com/Tnze/go-mc/server/ecs"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Client struct {
|
type Client struct {
|
||||||
*net.Conn
|
*net.Conn
|
||||||
Protocol int32
|
Protocol int32
|
||||||
ecs.Index
|
|
||||||
packetQueue *PacketQueue
|
packetQueue *PacketQueue
|
||||||
errChan chan error
|
errChan chan error
|
||||||
}
|
}
|
||||||
|
@ -1,96 +0,0 @@
|
|||||||
package ecs
|
|
||||||
|
|
||||||
import (
|
|
||||||
"golang.org/x/exp/constraints"
|
|
||||||
"math/bits"
|
|
||||||
"unsafe"
|
|
||||||
)
|
|
||||||
|
|
||||||
const uintsize = uint(unsafe.Sizeof(BitSet{}.values[0]))
|
|
||||||
|
|
||||||
type BitSet struct {
|
|
||||||
// TODO: this is not a BitSet, I'm just testing
|
|
||||||
values []uint
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *BitSet) Set(i Index) (old bool) {
|
|
||||||
index := uint(i) / uintsize
|
|
||||||
offset := uint(i) % uintsize
|
|
||||||
if index >= uint(len(b.values)) {
|
|
||||||
if index < uint(cap(b.values)) {
|
|
||||||
b.values = b.values[:index]
|
|
||||||
} else {
|
|
||||||
newValues := make([]uint, index+1)
|
|
||||||
copy(newValues, b.values)
|
|
||||||
b.values = newValues
|
|
||||||
}
|
|
||||||
}
|
|
||||||
v := &b.values[index]
|
|
||||||
mask := uint(1 << offset)
|
|
||||||
old = *v&mask != 0
|
|
||||||
*v |= mask
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *BitSet) Unset(i Index) (old bool) {
|
|
||||||
index := uint(i) / uintsize
|
|
||||||
offset := uint(i) % uintsize
|
|
||||||
if index < uint(len(b.values)) {
|
|
||||||
v := &b.values[index]
|
|
||||||
mask := uint(1 << offset)
|
|
||||||
old = *v&mask != 0
|
|
||||||
*v &= ^mask
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *BitSet) Contains(i Index) bool {
|
|
||||||
index := uint(i) / uintsize
|
|
||||||
offset := uint(i) % uintsize
|
|
||||||
return index < uint(len(b.values)) && b.values[index]&(1<<offset) != 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *BitSet) And(other *BitSet) *BitSet {
|
|
||||||
result := BitSet{values: make([]uint, min(len(b.values), len(other.values)))}
|
|
||||||
for i := range b.values {
|
|
||||||
result.values[i] = b.values[i] & other.values[i]
|
|
||||||
}
|
|
||||||
return &result
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *BitSet) AndNot(other BitSet) *BitSet {
|
|
||||||
result := BitSet{values: make([]uint, len(b.values))}
|
|
||||||
for i := range b.values {
|
|
||||||
if i < len(other.values) {
|
|
||||||
result.values[i] = b.values[i] & ^other.values[i]
|
|
||||||
} else {
|
|
||||||
result.values[i] = b.values[i]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return &result
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *BitSet) Range(f func(eid Index)) {
|
|
||||||
for i, v := range b.values {
|
|
||||||
base := int(unsafe.Sizeof(v)) * i
|
|
||||||
for v != 0 {
|
|
||||||
p := bits.TrailingZeros(v)
|
|
||||||
f(Index(base + p))
|
|
||||||
v ^= 1 << p
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func max[T constraints.Integer](a, b T) T {
|
|
||||||
if a > b {
|
|
||||||
return a
|
|
||||||
}
|
|
||||||
return b
|
|
||||||
}
|
|
||||||
|
|
||||||
func min[T constraints.Integer](a, b T) T {
|
|
||||||
if a < b {
|
|
||||||
return a
|
|
||||||
}
|
|
||||||
return b
|
|
||||||
}
|
|
@ -1,28 +0,0 @@
|
|||||||
package ecs
|
|
||||||
|
|
||||||
import (
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestBitSet_And(t *testing.T) {
|
|
||||||
var set1, set2 BitSet
|
|
||||||
|
|
||||||
set1.Set(1)
|
|
||||||
set1.Set(3)
|
|
||||||
set1.Set(40)
|
|
||||||
|
|
||||||
set2.Set(2)
|
|
||||||
set2.Set(3)
|
|
||||||
set2.Set(9)
|
|
||||||
set2.Set(40)
|
|
||||||
|
|
||||||
var results []Index
|
|
||||||
set1.And(&set2).Range(func(eid Index) {
|
|
||||||
results = append(results, eid)
|
|
||||||
})
|
|
||||||
want := []Index{3, 40}
|
|
||||||
if !reflect.DeepEqual(results, want) {
|
|
||||||
t.Errorf("want %v, got: %v", want, results)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,44 +0,0 @@
|
|||||||
package ecs
|
|
||||||
|
|
||||||
import "sync"
|
|
||||||
|
|
||||||
type Dispatcher struct {
|
|
||||||
waiters map[string][]*sync.WaitGroup
|
|
||||||
tasks []func(w *World, wg *sync.WaitGroup)
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewDispatcher() *Dispatcher {
|
|
||||||
return &Dispatcher{
|
|
||||||
waiters: make(map[string][]*sync.WaitGroup),
|
|
||||||
tasks: nil,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *Dispatcher) Add(s System, name string, deps []string) {
|
|
||||||
var start sync.WaitGroup
|
|
||||||
start.Add(len(deps))
|
|
||||||
for _, dep := range deps {
|
|
||||||
if wg, ok := d.waiters[dep]; ok {
|
|
||||||
d.waiters[dep] = append(wg, &start)
|
|
||||||
} else {
|
|
||||||
panic("Unknown deps: " + dep)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
d.tasks = append(d.tasks, func(w *World, done *sync.WaitGroup) {
|
|
||||||
start.Wait()
|
|
||||||
defer done.Done()
|
|
||||||
s.Update(w)
|
|
||||||
for _, wg := range d.waiters[name] {
|
|
||||||
wg.Done()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *Dispatcher) Run(w *World) {
|
|
||||||
var wg sync.WaitGroup
|
|
||||||
wg.Add(len(d.tasks))
|
|
||||||
for _, f := range d.tasks {
|
|
||||||
go f(w, &wg)
|
|
||||||
}
|
|
||||||
wg.Wait()
|
|
||||||
}
|
|
@ -1,34 +0,0 @@
|
|||||||
package ecs
|
|
||||||
|
|
||||||
import "testing"
|
|
||||||
|
|
||||||
func Test_common(t *testing.T) {
|
|
||||||
// W
|
|
||||||
w := NewWorld()
|
|
||||||
// C
|
|
||||||
type pos [2]int
|
|
||||||
type vel [2]int
|
|
||||||
Register[pos, *HashMapStorage[pos]](w)
|
|
||||||
Register[vel, *HashMapStorage[vel]](w)
|
|
||||||
// E
|
|
||||||
e1 := w.CreateEntity(pos{0, 0})
|
|
||||||
w.CreateEntity(vel{1, 2})
|
|
||||||
w.CreateEntity(pos{1, 2}, vel{2, 0})
|
|
||||||
// S
|
|
||||||
s1 := FuncSystem(func(p *pos) {
|
|
||||||
t.Log("system 1", p)
|
|
||||||
})
|
|
||||||
s2 := FuncSystem(func(p *pos, v *vel) {
|
|
||||||
t.Log("system 2", p, v)
|
|
||||||
})
|
|
||||||
s3 := FuncSystem(func(p pos, v *vel) {
|
|
||||||
t.Log("system 2", p, v)
|
|
||||||
})
|
|
||||||
// Run
|
|
||||||
s1.Update(w)
|
|
||||||
s2.Update(w)
|
|
||||||
s3.Update(w)
|
|
||||||
|
|
||||||
w.DeleteEntity(e1)
|
|
||||||
s1.Update(w)
|
|
||||||
}
|
|
@ -1,75 +0,0 @@
|
|||||||
package ecs
|
|
||||||
|
|
||||||
import (
|
|
||||||
"reflect"
|
|
||||||
"strconv"
|
|
||||||
"unsafe"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Index uint32
|
|
||||||
|
|
||||||
type Storage[T any] interface {
|
|
||||||
Init()
|
|
||||||
GetValue(eid Index) *T
|
|
||||||
SetValue(eid Index, v T)
|
|
||||||
DelValue(eid Index)
|
|
||||||
}
|
|
||||||
|
|
||||||
type HashMapStorage[T any] struct {
|
|
||||||
values map[Index]*T
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *HashMapStorage[T]) Init() { h.values = make(map[Index]*T) }
|
|
||||||
func (h *HashMapStorage[T]) Len() int { return len(h.values) }
|
|
||||||
func (h *HashMapStorage[T]) GetValue(eid Index) *T { return h.values[eid] }
|
|
||||||
func (h *HashMapStorage[T]) SetValue(eid Index, v T) { h.values[eid] = &v }
|
|
||||||
func (h *HashMapStorage[T]) DelValue(eid Index) { delete(h.values, eid) }
|
|
||||||
func (h *HashMapStorage[T]) Range(f func(eid Index, value *T)) {
|
|
||||||
for i, v := range h.values {
|
|
||||||
f(i, v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type NullStorage[T any] struct{}
|
|
||||||
|
|
||||||
func (NullStorage[T]) Init() {
|
|
||||||
var v T
|
|
||||||
if size := unsafe.Sizeof(v); size != 0 {
|
|
||||||
typeName := reflect.TypeOf(v).String()
|
|
||||||
typeSize := strconv.Itoa(int(size))
|
|
||||||
panic("NullStorage can only be used with ZST, " + typeName + " has size of " + typeSize)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
func (NullStorage[T]) GetValue(eid Index) *T { return nil }
|
|
||||||
func (NullStorage[T]) SetValue(eid Index, v T) {}
|
|
||||||
func (NullStorage[T]) DelValue(eid Index) {}
|
|
||||||
|
|
||||||
type MaskedStorage[T any] struct {
|
|
||||||
BitSet
|
|
||||||
Storage[T]
|
|
||||||
Len int
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *MaskedStorage[T]) Init() {
|
|
||||||
m.Storage.Init()
|
|
||||||
}
|
|
||||||
func (m *MaskedStorage[T]) GetValue(eid Index) *T {
|
|
||||||
if m.Contains(eid) {
|
|
||||||
return m.Storage.GetValue(eid)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
func (m *MaskedStorage[T]) GetValueAny(eid Index) any { return m.GetValue(eid) }
|
|
||||||
func (m *MaskedStorage[T]) SetValue(eid Index, v T) {
|
|
||||||
if !m.BitSet.Set(eid) {
|
|
||||||
m.Len++
|
|
||||||
}
|
|
||||||
m.Storage.SetValue(eid, v)
|
|
||||||
}
|
|
||||||
func (m *MaskedStorage[T]) SetAny(eid Index, v any) { m.SetValue(eid, v.(T)) }
|
|
||||||
func (m *MaskedStorage[T]) DelValue(eid Index) {
|
|
||||||
if m.BitSet.Unset(eid) {
|
|
||||||
m.Len--
|
|
||||||
}
|
|
||||||
m.Storage.DelValue(eid)
|
|
||||||
}
|
|
@ -1,70 +0,0 @@
|
|||||||
package ecs
|
|
||||||
|
|
||||||
import (
|
|
||||||
"reflect"
|
|
||||||
)
|
|
||||||
|
|
||||||
type System interface {
|
|
||||||
Update(w *World)
|
|
||||||
}
|
|
||||||
|
|
||||||
type funcsystem struct {
|
|
||||||
update func(w *World)
|
|
||||||
}
|
|
||||||
|
|
||||||
func FuncSystem(F any) System {
|
|
||||||
type Storage interface {
|
|
||||||
GetValueAny(eid Index) any
|
|
||||||
And(*BitSet) *BitSet
|
|
||||||
Range(f func(eid Index))
|
|
||||||
}
|
|
||||||
f := reflect.ValueOf(F)
|
|
||||||
in := f.Type().NumIn()
|
|
||||||
argTypes := make([]reflect.Type, in)
|
|
||||||
needCopy := make([]bool, in)
|
|
||||||
for i := 0; i < in; i++ {
|
|
||||||
if t := f.Type().In(i); t.Kind() == reflect.Pointer {
|
|
||||||
argTypes[i] = t.Elem()
|
|
||||||
} else {
|
|
||||||
argTypes[i] = t
|
|
||||||
needCopy[i] = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return &funcsystem{
|
|
||||||
update: func(w *World) {
|
|
||||||
storages := make([]reflect.Value, in)
|
|
||||||
for i := 0; i < in; i++ {
|
|
||||||
storages[i] = reflect.ValueOf(w.GetResourceRaw(argTypes[i]))
|
|
||||||
}
|
|
||||||
args := make([]reflect.Value, len(storages))
|
|
||||||
if len(storages) > 0 {
|
|
||||||
set := reflect.Indirect(storages[0]).FieldByName("BitSet").Addr()
|
|
||||||
for _, v := range storages[1:] {
|
|
||||||
p := reflect.Indirect(v).FieldByName("BitSet").Addr()
|
|
||||||
set = set.MethodByName("And").Call([]reflect.Value{p})[0]
|
|
||||||
}
|
|
||||||
set.MethodByName("Range").Call([]reflect.Value{
|
|
||||||
reflect.ValueOf(func(eid Index) {
|
|
||||||
for i := range args {
|
|
||||||
arg := storages[i].MethodByName("GetValue").Call([]reflect.Value{reflect.ValueOf(eid)})[0]
|
|
||||||
if arg.IsNil() {
|
|
||||||
args[i] = reflect.Zero(argTypes[i])
|
|
||||||
} else if needCopy[i] {
|
|
||||||
args[i] = arg.Elem()
|
|
||||||
} else {
|
|
||||||
args[i] = arg
|
|
||||||
}
|
|
||||||
}
|
|
||||||
f.Call(args)
|
|
||||||
}),
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
f.Call(args)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *funcsystem) Update(w *World) {
|
|
||||||
f.update(w)
|
|
||||||
}
|
|
@ -1,13 +0,0 @@
|
|||||||
package ecs
|
|
||||||
|
|
||||||
type PositionComponent struct {
|
|
||||||
X, Y int
|
|
||||||
}
|
|
||||||
|
|
||||||
type MySystem1 struct {
|
|
||||||
*PositionComponent
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *MySystem1) Update(w *World) {
|
|
||||||
|
|
||||||
}
|
|
@ -1,90 +0,0 @@
|
|||||||
package ecs
|
|
||||||
|
|
||||||
import (
|
|
||||||
"reflect"
|
|
||||||
"sync/atomic"
|
|
||||||
)
|
|
||||||
|
|
||||||
type World struct {
|
|
||||||
resources map[reflect.Type]any
|
|
||||||
maxEID Index
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewWorld() *World {
|
|
||||||
return &World{resources: make(map[reflect.Type]any)}
|
|
||||||
}
|
|
||||||
|
|
||||||
func SetResource[Res any](w *World, v Res) *Res {
|
|
||||||
w.resources[reflect.TypeOf(v)] = &v
|
|
||||||
return &v
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *World) Remove(resource any) any {
|
|
||||||
t := reflect.ValueOf(resource).Type()
|
|
||||||
resource = w.resources[t]
|
|
||||||
delete(w.resources, t)
|
|
||||||
return resource
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetResource[Res any](w *World) *Res {
|
|
||||||
var res Res
|
|
||||||
t := reflect.TypeOf(res)
|
|
||||||
if v, ok := w.resources[t]; ok {
|
|
||||||
return v.(*Res)
|
|
||||||
}
|
|
||||||
panic("Resource " + t.Name() + " not found")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *World) GetResourceRaw(t reflect.Type) any {
|
|
||||||
v, _ := w.resources[t]
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetComponent[T any](w *World) *MaskedStorage[T] {
|
|
||||||
var value T
|
|
||||||
t := reflect.ValueOf(value).Type()
|
|
||||||
if res, ok := w.resources[t]; ok {
|
|
||||||
return res.(*MaskedStorage[T])
|
|
||||||
}
|
|
||||||
panic("Component " + t.Name() + " not found")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Register the component with the storage.
|
|
||||||
//
|
|
||||||
// Will be changed to func (w *World) Register[C Component]() after Go support it
|
|
||||||
func Register[T any, S Storage[T]](w *World) {
|
|
||||||
var value T
|
|
||||||
t := reflect.TypeOf(value)
|
|
||||||
if _, ok := w.resources[t]; ok {
|
|
||||||
panic("Component " + t.Name() + " already exist")
|
|
||||||
}
|
|
||||||
var storage S
|
|
||||||
var storageInt Storage[T]
|
|
||||||
storageType := reflect.TypeOf(storage)
|
|
||||||
if storageType.Kind() == reflect.Pointer {
|
|
||||||
storageInt = reflect.New(storageType.Elem()).Interface().(Storage[T])
|
|
||||||
} else {
|
|
||||||
storageInt = storage
|
|
||||||
}
|
|
||||||
ms := MaskedStorage[T]{Storage: storageInt}
|
|
||||||
ms.Init()
|
|
||||||
w.resources[t] = &ms
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *World) CreateEntity(components ...any) (i Index) {
|
|
||||||
type Storage interface{ SetAny(Index, any) }
|
|
||||||
eid := Index(atomic.AddUint32((*uint32)(&w.maxEID), 1))
|
|
||||||
for _, c := range components {
|
|
||||||
w.resources[reflect.TypeOf(c)].(Storage).SetAny(eid, c)
|
|
||||||
}
|
|
||||||
return eid
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *World) DeleteEntity(eid Index) {
|
|
||||||
type Storage interface{ Del(eid Index) }
|
|
||||||
for _, r := range w.resources {
|
|
||||||
if c, ok := r.(Storage); ok {
|
|
||||||
c.Del(eid)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -9,8 +9,6 @@ import (
|
|||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
|
|
||||||
"github.com/Tnze/go-mc/net"
|
"github.com/Tnze/go-mc/net"
|
||||||
pk "github.com/Tnze/go-mc/net/packet"
|
|
||||||
"github.com/Tnze/go-mc/server/ecs"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type GamePlay interface {
|
type GamePlay interface {
|
||||||
@ -22,13 +20,15 @@ type GamePlay interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Game struct {
|
type Game struct {
|
||||||
*ecs.World
|
|
||||||
*ecs.Dispatcher
|
|
||||||
WorldLocker sync.Mutex
|
WorldLocker sync.Mutex
|
||||||
handlers map[int32][]*PacketHandler
|
handlers map[int32][]*PacketHandler
|
||||||
components []Component
|
components []Component
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (g *Game) AcceptPlayer(name string, id uuid.UUID, protocol int32, conn *net.Conn) {
|
||||||
|
conn.Close()
|
||||||
|
}
|
||||||
|
|
||||||
type PacketHandler struct {
|
type PacketHandler struct {
|
||||||
ID int32
|
ID int32
|
||||||
F packetHandlerFunc
|
F packetHandlerFunc
|
||||||
@ -45,16 +45,12 @@ type Component interface {
|
|||||||
|
|
||||||
func NewGame(components ...Component) *Game {
|
func NewGame(components ...Component) *Game {
|
||||||
g := &Game{
|
g := &Game{
|
||||||
World: ecs.NewWorld(),
|
|
||||||
Dispatcher: ecs.NewDispatcher(),
|
|
||||||
handlers: make(map[int32][]*PacketHandler),
|
handlers: make(map[int32][]*PacketHandler),
|
||||||
components: components,
|
components: components,
|
||||||
}
|
}
|
||||||
for _, c := range components {
|
for _, c := range components {
|
||||||
c.Init(g)
|
c.Init(g)
|
||||||
}
|
}
|
||||||
ecs.Register[Client, *ecs.HashMapStorage[Client]](g.World)
|
|
||||||
ecs.Register[Player, *ecs.HashMapStorage[Player]](g.World)
|
|
||||||
return g
|
return g
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -71,65 +67,10 @@ func (g *Game) Run(ctx context.Context) {
|
|||||||
select {
|
select {
|
||||||
case <-ticker.C:
|
case <-ticker.C:
|
||||||
g.WorldLocker.Lock()
|
g.WorldLocker.Lock()
|
||||||
g.Dispatcher.Run(g.World)
|
//g.Dispatcher.Run(g.World)
|
||||||
g.WorldLocker.Unlock()
|
g.WorldLocker.Unlock()
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *Game) AcceptPlayer(name string, id uuid.UUID, protocol int32, conn *net.Conn) {
|
|
||||||
g.WorldLocker.Lock()
|
|
||||||
eid := g.CreateEntity(
|
|
||||||
Client{
|
|
||||||
Conn: conn,
|
|
||||||
Protocol: protocol,
|
|
||||||
packetQueue: NewPacketQueue(),
|
|
||||||
errChan: make(chan error, 1),
|
|
||||||
},
|
|
||||||
Player{
|
|
||||||
UUID: id,
|
|
||||||
Name: name,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
c := ecs.GetComponent[Client](g.World).GetValue(eid)
|
|
||||||
p := ecs.GetComponent[Player](g.World).GetValue(eid)
|
|
||||||
g.WorldLocker.Unlock()
|
|
||||||
defer c.packetQueue.Close()
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
for {
|
|
||||||
packet, ok := c.packetQueue.Pull()
|
|
||||||
if !ok {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
err := c.Conn.WritePacket(packet)
|
|
||||||
if err != nil {
|
|
||||||
c.PutErr(err)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
var err error
|
|
||||||
for _, component := range g.components {
|
|
||||||
component.ClientJoin(c, p)
|
|
||||||
defer func(cmp Component) { cmp.ClientLeft(c, p, err) }(component)
|
|
||||||
}
|
|
||||||
|
|
||||||
var packet pk.Packet
|
|
||||||
for {
|
|
||||||
if err = c.ReadPacket(&packet); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for _, ph := range g.handlers[packet.ID] {
|
|
||||||
if err = ph.F(c, p, Packet758(packet)); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if err = c.GetErr(); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -8,7 +8,6 @@ import (
|
|||||||
|
|
||||||
"github.com/Tnze/go-mc/data/packetid"
|
"github.com/Tnze/go-mc/data/packetid"
|
||||||
pk "github.com/Tnze/go-mc/net/packet"
|
pk "github.com/Tnze/go-mc/net/packet"
|
||||||
"github.com/Tnze/go-mc/server/ecs"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// keepAliveInterval represents the interval when the server sends keep alive
|
// keepAliveInterval represents the interval when the server sends keep alive
|
||||||
@ -58,11 +57,6 @@ func (k *KeepAlive) AddPlayerDelayUpdateHandler(f func(p *Client, delay time.Dur
|
|||||||
|
|
||||||
// Init implement Component for KeepAlive
|
// Init implement Component for KeepAlive
|
||||||
func (k *KeepAlive) Init(g *Game) {
|
func (k *KeepAlive) Init(g *Game) {
|
||||||
ecs.Register[ClientDelay, *ecs.HashMapStorage[ClientDelay]](g.World)
|
|
||||||
k.AddPlayerDelayUpdateHandler(func(p *Client, delay time.Duration) {
|
|
||||||
c := ClientDelay{Delay: delay}
|
|
||||||
ecs.GetComponent[ClientDelay](g.World).SetValue(p.Index, c)
|
|
||||||
})
|
|
||||||
g.AddHandler(&PacketHandler{
|
g.AddHandler(&PacketHandler{
|
||||||
ID: packetid.ServerboundKeepAlive,
|
ID: packetid.ServerboundKeepAlive,
|
||||||
F: func(client *Client, player *Player, packet Packet758) error {
|
F: func(client *Client, player *Player, packet Packet758) error {
|
||||||
|
@ -1,124 +0,0 @@
|
|||||||
package player
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"github.com/Tnze/go-mc/data/packetid"
|
|
||||||
pk "github.com/Tnze/go-mc/net/packet"
|
|
||||||
"github.com/Tnze/go-mc/server"
|
|
||||||
"github.com/Tnze/go-mc/server/ecs"
|
|
||||||
"github.com/Tnze/go-mc/server/world"
|
|
||||||
)
|
|
||||||
|
|
||||||
type PlayerProfile struct {
|
|
||||||
Dim ecs.Index
|
|
||||||
}
|
|
||||||
|
|
||||||
type playerSpawnSystem struct {
|
|
||||||
storage
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p playerSpawnSystem) Update(w *ecs.World) {
|
|
||||||
clients := ecs.GetComponent[server.Client](w)
|
|
||||||
players := ecs.GetComponent[server.Player](w)
|
|
||||||
pos, rot := ecs.GetComponent[server.Pos](w), ecs.GetComponent[server.Rot](w)
|
|
||||||
profiles := ecs.GetComponent[PlayerProfile](w)
|
|
||||||
dimensionRes := ecs.GetResource[world.DimensionList](w)
|
|
||||||
players.AndNot(profiles.BitSet).Range(func(eid ecs.Index) {
|
|
||||||
player := players.GetValue(eid)
|
|
||||||
client := clients.GetValue(eid)
|
|
||||||
profile, err := p.GetPlayer(player.UUID)
|
|
||||||
if err != nil {
|
|
||||||
client.PutErr(fmt.Errorf("read player data fail: %w", err))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
dim, ok := dimensionRes.Find(profile.Dimension)
|
|
||||||
if !ok {
|
|
||||||
panic("dimension " + profile.Dimension + " not found")
|
|
||||||
}
|
|
||||||
profiles.SetValue(eid, PlayerProfile{Dim: dim})
|
|
||||||
pos.SetValue(eid, server.Pos{
|
|
||||||
X: profile.Pos[0],
|
|
||||||
Y: profile.Pos[1],
|
|
||||||
Z: profile.Pos[2],
|
|
||||||
})
|
|
||||||
rot.SetValue(eid, server.Rot{
|
|
||||||
Yaw: profile.Rotation[0],
|
|
||||||
Pitch: profile.Rotation[1],
|
|
||||||
})
|
|
||||||
client.WritePacket(server.Packet758(pk.Marshal(
|
|
||||||
packetid.ClientboundLogin,
|
|
||||||
pk.Int(eid), // Entity ID
|
|
||||||
pk.Boolean(false), // Is hardcore
|
|
||||||
pk.Byte(profile.PlayerGameType), // Gamemode
|
|
||||||
pk.Byte(-1), // Prev Gamemode
|
|
||||||
dimensionRes,
|
|
||||||
pk.NBT(dimensionRes.DimCodecSNBT),
|
|
||||||
pk.NBT(dimensionRes.DimSNBT),
|
|
||||||
pk.Identifier(profile.Dimension), // World Name
|
|
||||||
pk.Long(1234567), // Hashed seed
|
|
||||||
pk.VarInt(0), // Max Players (Ignored by client)
|
|
||||||
pk.VarInt(15), // View Distance
|
|
||||||
pk.VarInt(15), // Simulation Distance
|
|
||||||
pk.Boolean(false), // Reduced Debug Info
|
|
||||||
pk.Boolean(true), // Enable respawn screen
|
|
||||||
pk.Boolean(false), // Is Debug
|
|
||||||
pk.Boolean(true), // Is Flat
|
|
||||||
)))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func SpawnSystem(g *server.Game, playerdataPath string) {
|
|
||||||
ecs.Register[PlayerProfile, *ecs.HashMapStorage[PlayerProfile]](g.World)
|
|
||||||
g.Dispatcher.Add(
|
|
||||||
playerSpawnSystem{storage: storage{playerdataPath}},
|
|
||||||
"go-mc:player:SpawnSystem",
|
|
||||||
nil,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// PosAndRotSystem add a system to g.Dispatcher that
|
|
||||||
// receive player movement packets and update Pos and Rot component
|
|
||||||
// Require component Pos and Rot to be registered before.
|
|
||||||
func PosAndRotSystem(g *server.Game) {
|
|
||||||
type posUpdate struct {
|
|
||||||
ecs.Index
|
|
||||||
server.Pos
|
|
||||||
}
|
|
||||||
updateChan := make(chan posUpdate)
|
|
||||||
ecs.Register[server.Pos, *ecs.HashMapStorage[server.Pos]](g.World)
|
|
||||||
ecs.Register[server.Rot, *ecs.HashMapStorage[server.Rot]](g.World)
|
|
||||||
g.Dispatcher.Add(ecs.FuncSystem(func() {
|
|
||||||
posStorage := ecs.GetComponent[server.Pos](g.World)
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case event := <-updateChan:
|
|
||||||
if v := posStorage.GetValue(event.Index); v != nil {
|
|
||||||
*v = event.Pos
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}), "go-mc:player:PosAndRotSystem", nil)
|
|
||||||
|
|
||||||
g.AddHandler(&server.PacketHandler{
|
|
||||||
ID: packetid.ServerboundMovePlayerPos,
|
|
||||||
F: func(client *server.Client, player *server.Player, packet server.Packet758) error {
|
|
||||||
var X, FeetY, Z pk.Double
|
|
||||||
var OnGround pk.Boolean
|
|
||||||
err := pk.Packet(packet).Scan(&X, &FeetY, &Z, &OnGround)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
updateChan <- posUpdate{
|
|
||||||
Index: client.Index,
|
|
||||||
Pos: server.Pos{
|
|
||||||
X: float64(X),
|
|
||||||
Y: float64(FeetY),
|
|
||||||
Z: float64(Z),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
@ -1,163 +0,0 @@
|
|||||||
package player
|
|
||||||
|
|
||||||
//import (
|
|
||||||
// "context"
|
|
||||||
// "io"
|
|
||||||
// "time"
|
|
||||||
//
|
|
||||||
// "github.com/Tnze/go-mc/data/packetid"
|
|
||||||
// pk "github.com/Tnze/go-mc/net/packet"
|
|
||||||
// "github.com/Tnze/go-mc/server"
|
|
||||||
// "github.com/Tnze/go-mc/server/ecs"
|
|
||||||
//)
|
|
||||||
//
|
|
||||||
//type PlayerInfo struct {
|
|
||||||
// updateDelay chan playerDelayUpdate
|
|
||||||
// quit chan clientAndPlayer
|
|
||||||
//}
|
|
||||||
//type clientAndPlayer struct {
|
|
||||||
// *server.Client
|
|
||||||
// *server.Player
|
|
||||||
//}
|
|
||||||
//
|
|
||||||
//type playerInfoList struct {
|
|
||||||
// players ecs.MaskedStorage[server.Player]
|
|
||||||
// delays ecs.MaskedStorage[server.ClientDelay]
|
|
||||||
//}
|
|
||||||
//
|
|
||||||
//func (p playerInfoList) WriteTo(w io.Writer) (n int64, err error) {
|
|
||||||
// n, err = pk.VarInt(p.players.Len).WriteTo(w)
|
|
||||||
// if err != nil {
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
// var n1 int64
|
|
||||||
// p.players.And(p.delays.BitSetLike).Range(func(eid ecs.Index) {
|
|
||||||
// p := playerDelayUpdate{
|
|
||||||
// player: p.players.Get(eid),
|
|
||||||
// delay: p.delays.Get(eid).Delay,
|
|
||||||
// }
|
|
||||||
// n1, err = p.WriteTo(w)
|
|
||||||
// n += n1
|
|
||||||
// if err != nil {
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
// })
|
|
||||||
// return
|
|
||||||
//}
|
|
||||||
//
|
|
||||||
//type playerDelayUpdate struct {
|
|
||||||
// player *server.Player
|
|
||||||
// delay time.Duration
|
|
||||||
//}
|
|
||||||
//
|
|
||||||
//func (p playerDelayUpdate) WriteTo(w io.Writer) (n int64, err error) {
|
|
||||||
// return pk.Tuple{
|
|
||||||
// pk.UUID(p.player.UUID),
|
|
||||||
// pk.VarInt(p.delay.Milliseconds()),
|
|
||||||
// }.WriteTo(w)
|
|
||||||
//}
|
|
||||||
//
|
|
||||||
//const (
|
|
||||||
// actionAddPlayer = iota
|
|
||||||
// actionUpdateGamemode
|
|
||||||
// actionUpdateLatency
|
|
||||||
// actionUpdateDisplayName
|
|
||||||
// actionRemovePlayer
|
|
||||||
//)
|
|
||||||
//
|
|
||||||
//type DelaySource interface {
|
|
||||||
// AddPlayerDelayUpdateHandler(f func(c *server.Client, p *server.Player, delay time.Duration))
|
|
||||||
//}
|
|
||||||
//
|
|
||||||
//func NewPlayerInfo(delaySource DelaySource) *PlayerInfo {
|
|
||||||
// updateChan := make(chan playerDelayUpdate)
|
|
||||||
// p := &PlayerInfo{
|
|
||||||
// updateDelay: updateChan,
|
|
||||||
// quit: make(chan clientAndPlayer),
|
|
||||||
// }
|
|
||||||
// if delaySource != nil {
|
|
||||||
// delaySource.AddPlayerDelayUpdateHandler(func(client *server.Client, player *server.Player, delay time.Duration) {
|
|
||||||
// updateChan <- playerDelayUpdate{player: player, delay: delay}
|
|
||||||
// })
|
|
||||||
// }
|
|
||||||
// return p
|
|
||||||
//}
|
|
||||||
//
|
|
||||||
//type playerInfoSystemJoin struct{}
|
|
||||||
//
|
|
||||||
//func (p playerInfoSystemJoin) Update(w *ecs.World) {
|
|
||||||
// clients := ecs.GetComponent[server.Client](w)
|
|
||||||
// players := ecs.GetComponent[server.Player](w)
|
|
||||||
// delays := ecs.GetComponent[server.ClientDelay](w)
|
|
||||||
//}
|
|
||||||
//
|
|
||||||
//func (p *PlayerInfo) Init(g *server.Game) {
|
|
||||||
// var delayBuffer []playerDelayUpdate
|
|
||||||
// clients := ecs.GetComponent[server.Client](g.World)
|
|
||||||
// players := ecs.GetComponent[server.Player](g.World)
|
|
||||||
// delays := ecs.GetComponent[server.ClientDelay](g.World)
|
|
||||||
// g.Dispatcher.Add(ecs.FuncSystem(func(client *server.Client, player *server.Player, delay server.ClientDelay) {
|
|
||||||
// info := server.ClientDelay{Delay: 0}
|
|
||||||
// pack := server.Packet758(pk.Marshal(
|
|
||||||
// packetid.ClientboundPlayerInfo,
|
|
||||||
// pk.VarInt(actionAddPlayer),
|
|
||||||
// pk.VarInt(1),
|
|
||||||
//
|
|
||||||
// pk.UUID(player.UUID),
|
|
||||||
// pk.String(player.Name),
|
|
||||||
// pk.Array([]pk.FieldEncoder{}),
|
|
||||||
// pk.VarInt(profile.Gamemode),
|
|
||||||
// pk.VarInt(0),
|
|
||||||
// pk.Boolean(false),
|
|
||||||
// ))
|
|
||||||
// delays.Set(client.Index, info)
|
|
||||||
// clients.Range(func(eid ecs.Index) {
|
|
||||||
// clients.Get(eid).WritePacket(pack)
|
|
||||||
// })
|
|
||||||
// client.WritePacket(server.Packet758(pk.Marshal(
|
|
||||||
// packetid.ClientboundPlayerInfo,
|
|
||||||
// pk.VarInt(actionAddPlayer),
|
|
||||||
// playerInfoList{players: players, delays: delays},
|
|
||||||
// )))
|
|
||||||
// }), "PlayerInfoSystem:Join", nil)
|
|
||||||
// g.Dispatcher.Add(ecs.FuncSystem(func() {
|
|
||||||
// for {
|
|
||||||
// select {
|
|
||||||
// case cp := <-p.quit:
|
|
||||||
// pack := server.Packet758(pk.Marshal(
|
|
||||||
// packetid.ClientboundPlayerInfo,
|
|
||||||
// pk.VarInt(actionRemovePlayer),
|
|
||||||
// pk.VarInt(1),
|
|
||||||
// pk.UUID(cp.UUID),
|
|
||||||
// ))
|
|
||||||
// for _, p := range players.list {
|
|
||||||
// cp.WritePacket(pack)
|
|
||||||
// }
|
|
||||||
// case change := <-p.updateDelay:
|
|
||||||
// delayBuffer = append(delayBuffer, change)
|
|
||||||
// default:
|
|
||||||
// if len(delayBuffer) > 0 {
|
|
||||||
// pack := server.Packet758(pk.Marshal(
|
|
||||||
// packetid.ClientboundPlayerInfo,
|
|
||||||
// pk.VarInt(actionUpdateLatency),
|
|
||||||
// pk.Array(&delayBuffer),
|
|
||||||
// ))
|
|
||||||
// players.Range(func(eid ecs.Index) {
|
|
||||||
// players.Get(eid).(*server.Client).WritePacket(pack)
|
|
||||||
// })
|
|
||||||
// delayBuffer = delayBuffer[:0]
|
|
||||||
// }
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }), "PlayerInfoSystem", nil)
|
|
||||||
//}
|
|
||||||
//
|
|
||||||
//func (p *PlayerInfo) Run(context.Context) {}
|
|
||||||
//func (p *PlayerInfo) ClientJoin(client *server.Client, player *server.Player) {}
|
|
||||||
//func (p *PlayerInfo) ClientLeft(client *server.Client, player *server.Player) {
|
|
||||||
// p.quit <- clientAndPlayer{
|
|
||||||
// Client: client,
|
|
||||||
// Player: player,
|
|
||||||
// }
|
|
||||||
//}
|
|
@ -1,37 +0,0 @@
|
|||||||
package player
|
|
||||||
|
|
||||||
import (
|
|
||||||
"compress/gzip"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
|
|
||||||
"github.com/google/uuid"
|
|
||||||
|
|
||||||
"github.com/Tnze/go-mc/level"
|
|
||||||
"github.com/Tnze/go-mc/save"
|
|
||||||
)
|
|
||||||
|
|
||||||
type storage struct {
|
|
||||||
playerdataDir string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *storage) GetPlayer(id uuid.UUID) (data save.PlayerData, err error) {
|
|
||||||
filename := id.String() + ".dat"
|
|
||||||
|
|
||||||
f, err := os.Open(filepath.Join(s.playerdataDir, filename))
|
|
||||||
if err != nil {
|
|
||||||
return save.PlayerData{}, err
|
|
||||||
}
|
|
||||||
defer f.Close()
|
|
||||||
|
|
||||||
r, err := gzip.NewReader(f)
|
|
||||||
if err != nil {
|
|
||||||
return save.PlayerData{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return save.ReadPlayerData(r)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *storage) PutPlayer(pos level.ChunkPos, c *level.Chunk) (err error) {
|
|
||||||
return nil
|
|
||||||
}
|
|
@ -30,8 +30,8 @@ import (
|
|||||||
"github.com/Tnze/go-mc/net"
|
"github.com/Tnze/go-mc/net"
|
||||||
)
|
)
|
||||||
|
|
||||||
const ProtocolName = "1.18.2"
|
const ProtocolName = "1.19"
|
||||||
const ProtocolVersion = 758
|
const ProtocolVersion = 759
|
||||||
|
|
||||||
type Server struct {
|
type Server struct {
|
||||||
ListPingHandler
|
ListPingHandler
|
||||||
|
@ -1,17 +0,0 @@
|
|||||||
{
|
|
||||||
piglin_safe:0B,
|
|
||||||
natural:1B,
|
|
||||||
ambient_light:0.0000000000F,
|
|
||||||
infiniburn:"#minecraft:infiniburn_overworld",
|
|
||||||
respawn_anchor_works:0B,
|
|
||||||
has_skylight:1B,
|
|
||||||
bed_works:1B,
|
|
||||||
effects:"minecraft:overworld",
|
|
||||||
has_raids:1B,
|
|
||||||
logical_height:384,
|
|
||||||
coordinate_scale:1.0000000000D,
|
|
||||||
min_y:-64,
|
|
||||||
has_ceiling:0B,
|
|
||||||
ultrawarm:0B,
|
|
||||||
height:384
|
|
||||||
}
|
|
File diff suppressed because one or more lines are too long
@ -1,74 +0,0 @@
|
|||||||
package world
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"path/filepath"
|
|
||||||
|
|
||||||
"github.com/Tnze/go-mc/level"
|
|
||||||
"github.com/Tnze/go-mc/save"
|
|
||||||
"github.com/Tnze/go-mc/save/region"
|
|
||||||
)
|
|
||||||
|
|
||||||
// TODO: Cache regions and chunks
|
|
||||||
type storage struct {
|
|
||||||
regionDir string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *storage) GetChunk(pos level.ChunkPos) (lc *level.Chunk, err error) {
|
|
||||||
filename := fmt.Sprintf("r.%d.%d.mca", pos.X>>5, pos.Z>>5)
|
|
||||||
|
|
||||||
var r *region.Region
|
|
||||||
r, err = region.Open(filepath.Join(s.regionDir, filename))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer func() {
|
|
||||||
err2 := r.Close()
|
|
||||||
if err == nil && err2 != nil {
|
|
||||||
err = err2
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
sector, err := r.ReadSector(region.In(pos.X, pos.Z))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var sc save.Chunk
|
|
||||||
err = sc.Load(sector)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return level.ChunkFromSave(&sc)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *storage) PutChunk(pos level.ChunkPos, c *level.Chunk) (err error) {
|
|
||||||
var sc save.Chunk
|
|
||||||
err = level.ChunkToSave(c, &sc)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var data []byte
|
|
||||||
data, err = sc.Data(1)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
filename := fmt.Sprintf("r.%d.%d.mca", pos.X>>5, pos.Z>>5)
|
|
||||||
var r *region.Region
|
|
||||||
r, err = region.Open(filepath.Join(s.regionDir, filename))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer func() {
|
|
||||||
err2 := r.Close()
|
|
||||||
if err == nil && err2 != nil {
|
|
||||||
err = err2
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
x, z := region.In(pos.X, pos.Z)
|
|
||||||
err = r.WriteSector(x, z, data)
|
|
||||||
return
|
|
||||||
}
|
|
@ -1,63 +0,0 @@
|
|||||||
package world
|
|
||||||
|
|
||||||
import (
|
|
||||||
_ "embed"
|
|
||||||
"io"
|
|
||||||
"unsafe"
|
|
||||||
|
|
||||||
"github.com/Tnze/go-mc/nbt"
|
|
||||||
pk "github.com/Tnze/go-mc/net/packet"
|
|
||||||
"github.com/Tnze/go-mc/server"
|
|
||||||
"github.com/Tnze/go-mc/server/ecs"
|
|
||||||
)
|
|
||||||
|
|
||||||
//go:embed DimensionCodec.snbt
|
|
||||||
var dimensionCodecSNBT nbt.StringifiedMessage
|
|
||||||
|
|
||||||
//go:embed Dimension.snbt
|
|
||||||
var dimensionSNBT nbt.StringifiedMessage
|
|
||||||
|
|
||||||
type Dimension struct {
|
|
||||||
storage
|
|
||||||
Name string
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewDimension(name, path string) Dimension {
|
|
||||||
return Dimension{
|
|
||||||
Name: name,
|
|
||||||
storage: storage{regionDir: path},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type DimensionList struct {
|
|
||||||
Dims []ecs.Index
|
|
||||||
DimNames []string
|
|
||||||
DimCodecSNBT, DimSNBT nbt.StringifiedMessage
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *DimensionList) WriteTo(w io.Writer) (n int64, err error) {
|
|
||||||
return pk.Array(*(*[]pk.Identifier)(unsafe.Pointer(&d.DimNames))).WriteTo(w)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *DimensionList) Find(dim string) (ecs.Index, bool) {
|
|
||||||
for i, v := range d.DimNames {
|
|
||||||
if v == dim {
|
|
||||||
return d.Dims[i], true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0, false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *DimensionList) Add(id ecs.Index, name string) {
|
|
||||||
d.Dims = append(d.Dims, id)
|
|
||||||
d.DimNames = append(d.DimNames, name)
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewDimensionManager(g *server.Game) *DimensionList {
|
|
||||||
return ecs.SetResource(g.World, DimensionList{
|
|
||||||
Dims: nil,
|
|
||||||
DimNames: nil,
|
|
||||||
DimCodecSNBT: dimensionCodecSNBT,
|
|
||||||
DimSNBT: dimensionSNBT,
|
|
||||||
})
|
|
||||||
}
|
|
Reference in New Issue
Block a user