Palette support 1

This commit is contained in:
Tnze
2021-12-17 18:45:06 +08:00
parent ee33cedd79
commit 70b0fbf1b7
12 changed files with 2481 additions and 17 deletions

17
server/Dimension.snbt Normal file
View File

@ -0,0 +1,17 @@
{
piglin_safe: 0b,
natural: 1b,
ambient_light: 0.0f,
infiniburn: "minecraft:infiniburn_overworld",
respawn_anchor_works: 0b,
has_skylight: 1b,
bed_works: 1b,
effects: "minecraft:overworld",
has_raids: 1b,
min_y: 0,
height: 256,
logical_height: 256,
coordinate_scale: 1.0d,
ultrawarm: 0b,
has_ceiling: 0b
}

2093
server/DimensionCodec.snbt Normal file

File diff suppressed because it is too large Load Diff

104
server/dimension.go Normal file
View File

@ -0,0 +1,104 @@
package server
import (
"bytes"
"github.com/Tnze/go-mc/data/packetid"
pk "github.com/Tnze/go-mc/net/packet"
"github.com/Tnze/go-mc/save"
"io"
"sync"
)
type Dimension interface {
Info() DimInfo
PlayerJoin(p *Player)
PlayerQuit(p *Player)
}
type DimInfo struct {
Name string
HashedSeed uint64
}
type SimpleDim struct {
Columns map[struct{ X, Z int }]*struct {
sync.Mutex
}
}
type chunkData struct {
HeightMaps save.BitStorage
BlockState save.BitStorage
Biomes save.BitStorage
}
func (c *chunkData) WriteTo(w io.Writer) (int64, error) {
return pk.Tuple{
// Heightmaps
pk.NBT(struct {
MotionBlocking []uint64 `nbt:"MOTION_BLOCKING"`
}{c.HeightMaps.Longs()}),
pk.ByteArray(c.Data()), // TODO: Chunk Data
pk.VarInt(0), // TODO: Block Entity
}.WriteTo(w)
}
func (c *chunkData) Data() []byte {
var buff bytes.Buffer
_, _ = pk.Short(0).WriteTo(&buff)
return buff.Bytes()
}
type lightData struct {
SkyLightMask pk.BitSet
BlockLightMask pk.BitSet
SkyLight []pk.ByteArray
BlockLight []pk.ByteArray
}
func bitSetRev(set pk.BitSet) pk.BitSet {
rev := make(pk.BitSet, len(set))
for i := range rev {
rev[i] = ^set[i]
}
return rev
}
func (l *lightData) WriteTo(w io.Writer) (int64, error) {
return pk.Tuple{
pk.Boolean(true), // Trust Edges
l.SkyLightMask,
l.BlockLightMask,
bitSetRev(l.SkyLightMask),
bitSetRev(l.BlockLightMask),
pk.Array(l.SkyLight),
pk.Array(l.BlockLight),
}.WriteTo(w)
}
func (s *SimpleDim) PlayerJoin(p *Player) {
for pos, column := range s.Columns {
column.Lock()
packet := pk.Marshal(
packetid.ClientboundLevelChunkWithLight,
pk.Int(pos.X), pk.Int(pos.Z),
&chunkData{},
&lightData{
SkyLightMask: make(pk.BitSet, (16*16*16-1)>>6+1),
BlockLightMask: make(pk.BitSet, (16*16*16-1)>>6+1),
SkyLight: []pk.ByteArray{},
BlockLight: []pk.ByteArray{},
},
)
column.Unlock()
err := p.WritePacket(Packet757(packet))
if err != nil {
return
}
}
}
func (s *SimpleDim) PlayerQuit(p *Player) {
}

View File

@ -1,8 +1,15 @@
package server
import (
"github.com/Tnze/go-mc/net"
_ "embed"
"github.com/Tnze/go-mc/nbt"
"sync/atomic"
"github.com/google/uuid"
"github.com/Tnze/go-mc/data/packetid"
"github.com/Tnze/go-mc/net"
pk "github.com/Tnze/go-mc/net/packet"
)
type GamePlay interface {
@ -12,3 +19,50 @@ type GamePlay interface {
// You don't need to close the connection, but to keep not returning while the player is playing.
AcceptPlayer(name string, id uuid.UUID, protocol int32, conn *net.Conn)
}
type Game struct {
eid int32
Dim Dimension
}
//go:embed DimensionCodec.snbt
var dimensionCodecSNBT string
//go:embed Dimension.snbt
var dimensionSNBT string
func (g *Game) AcceptPlayer(name string, id uuid.UUID, protocol int32, conn *net.Conn) {
p := &Player{
Conn: conn,
EntityID: g.newEID(),
Gamemode: 1,
}
dimInfo := g.Dim.Info()
err := p.WritePacket(Packet757(pk.Marshal(
packetid.ClientboundLogin,
pk.Int(p.EntityID), // Entity ID
pk.Boolean(false), // Is hardcore
pk.Byte(p.Gamemode), // Gamemode
pk.Byte(-1), // Prev Gamemode
pk.Array([]pk.Identifier{pk.Identifier(dimInfo.Name)}),
pk.NBT(nbt.StringifiedMessage(dimensionCodecSNBT)),
pk.NBT(nbt.StringifiedMessage(dimensionSNBT)),
pk.Identifier(dimInfo.Name), // World Name
pk.Long(dimInfo.HashedSeed), // 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
)))
if err != nil {
return
}
g.Dim.PlayerJoin(p)
}
func (g *Game) newEID() int32 {
return atomic.AddInt32(&g.eid, 1)
}

View File

@ -20,10 +20,10 @@ type LoginHandler interface {
}
// LoginChecker is the interface to check if a player is allowed to log in the server.
// The checking could be anything, server player number, blacklist or whitelist.
// The checking could be anything, server player number, protocol version, blacklist or whitelist.
// If a player is not allowed to, the reason should be returned and will be sent to client by "LoginDisconnect" packet.
type LoginChecker interface {
CheckPlayer(name string, id uuid.UUID) (ok bool, reason chat.Message)
CheckPlayer(name string, id uuid.UUID, protocol int32) (ok bool, reason chat.Message)
}
// MojangLoginHandler is a standard LoginHandler that implement both online and offline login progress.
@ -92,7 +92,7 @@ func (d *MojangLoginHandler) AcceptLogin(conn *net.Conn, protocol int32) (name s
// check if player can join (whitelist, blacklist, server full or something else)
if d.LoginChecker != nil {
if ok, result := d.CheckPlayer(name, id); !ok {
if ok, result := d.CheckPlayer(name, id, protocol); !ok {
// player is not allowed to join the server
err = conn.WritePacket(pk.Marshal(
packetid.LoginDisconnect,

21
server/player.go Normal file
View File

@ -0,0 +1,21 @@
package server
import (
"github.com/Tnze/go-mc/net"
pk "github.com/Tnze/go-mc/net/packet"
)
type Player struct {
*net.Conn
EntityID int32
Gamemode byte
}
// Packet757 is a packet in protocol 757.
// We are using type system to force programmers to update packets.
type Packet757 pk.Packet
// WritePacket to player client. The type of parameter will update per version.
func (p *Player) WritePacket(packet Packet757) error {
return p.Conn.WritePacket(pk.Packet(packet))
}

View File

@ -2,6 +2,8 @@
// You can build the server you want by combining the various functional modules provided here.
// An example can be found in examples/frameworkServer.
//
// This package is under rapid development, and any API may be subject to break changes
//
// A server is roughly divided into two parts: Gate and GamePlay
//
// +---------------------------------------------------------------------+
@ -24,7 +26,9 @@
// The implement of Gameplay will provide later.
package server
import "github.com/Tnze/go-mc/net"
import (
"github.com/Tnze/go-mc/net"
)
const ProtocolName = "1.18.1"
const ProtocolVersion = 757