New server framework, an example, and compressed packet fixed.

This commit is contained in:
Tnze
2021-11-27 15:25:55 +08:00
parent b909621c58
commit ab63acbd7e
13 changed files with 2792 additions and 69 deletions

View File

@ -0,0 +1,159 @@
package main
import (
"container/list"
_ "embed"
"log"
"sync"
"github.com/Tnze/go-mc/chat"
"github.com/Tnze/go-mc/data/packetid"
"github.com/Tnze/go-mc/nbt"
"github.com/Tnze/go-mc/net"
pk "github.com/Tnze/go-mc/net/packet"
"github.com/Tnze/go-mc/server"
"github.com/google/uuid"
)
type MyServer struct {
PlayerList *list.List
PlayerListLock sync.Mutex
server.MojangLoginHandler
Settings struct {
Name string
MaxPlayer int
MOTD chat.Message
}
}
func main() {
var ms MyServer
ms.PlayerList = list.New()
ms.MojangLoginHandler.OnlineMode = true
ms.MojangLoginHandler.Threshold = 256
ms.Settings.Name = "MyServer"
ms.Settings.MaxPlayer = 20
ms.Settings.MOTD = chat.Message{Text: "A Minecraft Server ", Extra: []chat.Message{{Text: "Powered by go-mc", Color: "yellow"}}}
s := server.Server{
ListPingHandler: &ms,
LoginHandler: &ms,
GamePlay: &ms,
}
if err := s.Listen(":25565"); err != nil {
log.Fatalf("Listen error: %v", err)
}
}
func (m *MyServer) Name() string {
return m.Settings.Name
}
func (m *MyServer) Protocol() int {
return server.ProtocolVersion
}
func (m *MyServer) MaxPlayer() int {
return m.Settings.MaxPlayer
}
func (m *MyServer) OnlinePlayer() int {
m.PlayerListLock.Lock()
defer m.PlayerListLock.Unlock()
return m.PlayerList.Len()
}
func (m *MyServer) PlayerSamples() (sample []server.PlayerSample) {
m.PlayerListLock.Lock()
defer m.PlayerListLock.Unlock()
// get first 10 players
sample = make([]server.PlayerSample, 0, 10)
v := m.PlayerList.Front()
for i := 0; i < 10; i++ {
if v == nil {
break
}
sample = append(sample, v.Value.(server.PlayerSample))
v = v.Next()
}
return
}
func (m *MyServer) Description() chat.Message {
return m.Settings.MOTD
}
func (m *MyServer) AcceptPlayer(name string, id uuid.UUID, protocol int32, conn *net.Conn) {
// Add player into PlayerList
m.PlayerListLock.Lock()
elem := m.PlayerList.PushBack(server.PlayerSample{
Name: name,
ID: id,
})
m.PlayerListLock.Unlock()
defer func() {
m.PlayerListLock.Lock()
defer m.PlayerListLock.Unlock()
// remove player in PlayerList
m.PlayerList.Remove(elem)
}()
if err := m.joinGame(conn); err != nil {
log.Printf("Write packet fail: %v", err)
return
}
if err := m.playerPositionAndLook(conn); err != nil {
log.Printf("Write packet fail: %v", err)
return
}
var p pk.Packet
for {
err := conn.ReadPacket(&p)
if err != nil {
log.Printf("Read packet fail: %v", err)
break
}
log.Printf("Read packet: %#X", p.ID)
}
}
//go:embed DimensionCodec.snbt
var dimensionCodecSNBT string
//go:embed Dimension.snbt
var dimensionSNBT string
func (m *MyServer) joinGame(conn *net.Conn) error {
return conn.WritePacket(pk.Marshal(packetid.Login,
pk.Int(0), // EntityID
pk.Boolean(false), // Is hardcore
pk.UnsignedByte(1), // Gamemode
pk.Byte(1), // Previous Gamemode
pk.VarInt(1), // World Count
pk.Ary{Len: 1, Ary: []pk.Identifier{"world"}}, // World Names
pk.NBT(nbt.StringifiedMessage(dimensionCodecSNBT)), // Dimension codec
pk.NBT(nbt.StringifiedMessage(dimensionSNBT)), // Dimension
pk.Identifier("world"), // World Name
pk.Long(0), // Hashed Seed
pk.VarInt(m.Settings.MaxPlayer), // Max Players
pk.VarInt(15), // View Distance
pk.Boolean(false), // Reduced Debug Info
pk.Boolean(true), // Enable respawn screen
pk.Boolean(false), // Is Debug
pk.Boolean(true), // Is Flat
))
}
func (m *MyServer) playerPositionAndLook(conn *net.Conn) error {
return conn.WritePacket(pk.Marshal(packetid.PositionClientbound,
// https://wiki.vg/index.php?title=Protocol&oldid=16067#Player_Position_And_Look_.28clientbound.29
pk.Double(0), pk.Double(0), pk.Double(0), // XYZ
pk.Float(0), pk.Float(0), // Yaw Pitch
pk.Byte(0), // flag
pk.VarInt(0), // TP ID
pk.Boolean(false), // Dismount vehicle
))
}