player and dimension loader with ecs system
This commit is contained in:
112
server/player/player.go
Normal file
112
server/player/player.go
Normal file
@ -0,0 +1,112 @@
|
||||
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"
|
||||
"log"
|
||||
)
|
||||
|
||||
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)
|
||||
profiles := ecs.GetComponent[PlayerProfile](w)
|
||||
dimensionRes := ecs.GetResource[world.DimensionList](w)
|
||||
players.AndNot(profiles.BitSetLike).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
|
||||
}
|
||||
log.Println("load player info successes", profile)
|
||||
dim, ok := dimensionRes.Find(profile.Dimension)
|
||||
if !ok {
|
||||
panic("dimension " + profile.Dimension + " not found")
|
||||
}
|
||||
profiles.SetValue(eid, PlayerProfile{Dim: dim})
|
||||
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
|
||||
},
|
||||
})
|
||||
}
|
163
server/player/playerinfo.go
Normal file
163
server/player/playerinfo.go
Normal file
@ -0,0 +1,163 @@
|
||||
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,
|
||||
// }
|
||||
//}
|
37
server/player/pool.go
Normal file
37
server/player/pool.go
Normal file
@ -0,0 +1,37 @@
|
||||
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
|
||||
}
|
Reference in New Issue
Block a user