Merge branch 'pull/62' into 1.16-dev

This commit is contained in:
Tnze
2020-07-09 13:42:44 +08:00
10 changed files with 276 additions and 42 deletions

View File

@ -49,13 +49,16 @@ func NewClient() (c *Client) {
//PlayInfo content player info in server.
type PlayInfo struct {
Gamemode int //游戏模式
Hardcore bool //是否是极限模式
Dimension int //维度
Difficulty int //难度
LevelType string //地图类型
Gamemode int //游戏模式
Hardcore bool //是否是极限模式
Dimension int //维度
Difficulty int //难度
// LevelType string //地图类型 1.16删了
ViewDistance int //视距
ReducedDebugInfo bool //减少调试信息
WorldName string //当前世界的名字
IsDebug bool //调试
IsFlat bool //超平坦世界
// SpawnPosition Position //主世界出生点
}

View File

@ -9,7 +9,7 @@ import (
type eventBroker struct {
GameStart func() error
ChatMsg func(msg chat.Message, pos byte) error
ChatMsg func(msg chat.Message, pos byte, sender string) error
Disconnect func(reason chat.Message) error
HealthChange func() error
Die func() error

View File

@ -274,17 +274,18 @@ func handleSetSlotPacket(c *Client, p pk.Packet) error {
func handleChatMessagePacket(c *Client, p pk.Packet) (err error) {
var (
s chat.Message
pos pk.Byte
s chat.Message
pos pk.Byte
sender pk.UUID
)
err = p.Scan(&s, &pos)
err = p.Scan(&s, &pos, &sender)
if err != nil {
return err
}
if c.Events.ChatMsg != nil {
err = c.Events.ChatMsg(s, byte(pos))
err = c.Events.ChatMsg(s, byte(pos), string(sender.Encode()))
}
return err
@ -326,17 +327,25 @@ func handleUpdateHealthPacket(c *Client, p pk.Packet) (err error) {
func handleJoinGamePacket(c *Client, p pk.Packet) error {
var (
eid pk.Int
gamemode pk.UnsignedByte
eid pk.Int
gamemode pk.UnsignedByte
previousGm pk.UnsignedByte
worldCount pk.VarInt
worldNames pk.Identifier
_ pk.NBT
//dimensionCodec pk.NBT
dimension pk.Int
worldName pk.Identifier
hashedSeed pk.Long
maxPlayers pk.UnsignedByte
levelType pk.String
viewDistance pk.VarInt
rdi pk.Boolean // Reduced Debug Info
ers pk.Boolean // Enable respawn screen
isDebug pk.Boolean
isFlat pk.Boolean
)
err := p.Scan(&eid, &gamemode, &dimension, &hashedSeed, &maxPlayers, &levelType, &rdi, &ers)
err := p.Scan(&eid, &gamemode, &previousGm, &worldCount, &worldNames, &dimension, &worldName,
&hashedSeed, &maxPlayers, &rdi, &ers, &isDebug, &isFlat)
if err != nil {
return err
}
@ -345,9 +354,12 @@ func handleJoinGamePacket(c *Client, p pk.Packet) error {
c.Gamemode = int(gamemode & 0x7)
c.Hardcore = gamemode&0x8 != 0
c.Dimension = int(dimension)
c.LevelType = string(levelType)
c.WorldName = string(worldName)
c.ViewDistance = int(viewDistance)
c.ReducedDebugInfo = bool(rdi)
c.IsDebug = bool(isDebug)
c.IsFlat = bool(isFlat)
return nil
}
@ -441,13 +453,14 @@ func handleChunkDataPacket(c *Client, p pk.Packet) error {
var (
X, Z pk.Int
FullChunk pk.Boolean
IgnoreOldData pk.Boolean
PrimaryBitMask pk.VarInt
Heightmaps struct{}
Biomes = biomesData{fullChunk: (*bool)(&FullChunk)}
Data chunkData
BlockEntities blockEntities
)
if err := p.Scan(&X, &Z, &FullChunk, &PrimaryBitMask, pk.NBT{V: &Heightmaps}, &Biomes, &Data, &BlockEntities); err != nil {
if err := p.Scan(&X, &Z, &FullChunk, &IgnoreOldData, &PrimaryBitMask, pk.NBT{V: &Heightmaps}, &Biomes, &Data, &BlockEntities); err != nil {
return err
}
chunk, err := world.DecodeChunkColumn(int32(PrimaryBitMask), Data)

View File

@ -185,6 +185,7 @@ func loginAuth(AsTk, name, UUID string, shareSecret []byte, er encryptionRequest
}
PostRequest.Header.Set("User-agent", "go-mc")
PostRequest.Header.Set("Connection", "keep-alive")
PostRequest.Header.Set("Content-Type", "application/json")
resp, err := client.Do(PostRequest)
if err != nil {
return fmt.Errorf("post fail: %v", err)

View File

@ -13,7 +13,7 @@ import (
)
// ProtocolVersion , the protocol version number of minecraft net protocol
const ProtocolVersion = 578
const ProtocolVersion = 736
// JoinServer connect a Minecraft server for playing the game.
func (c *Client) JoinServer(addr string, port int) (err error) {

View File

@ -72,7 +72,7 @@ func onSound(name string, category int, x, y, z float64, volume, pitch float32)
return nil
}
func onChatMsg(c chat.Message, pos byte) error {
func onChatMsg(c chat.Message, pos byte, uuid string) error {
log.Println("Chat:", c)
return nil
}

View File

@ -38,7 +38,7 @@ func onGameStart() error {
return nil //if err isn't nil, HandleGame() will return it.
}
func onChatMsg(c chat.Message, pos byte) error {
func onChatMsg(c chat.Message, pos byte, uuid string) error {
log.Println("Chat:", c.ClearString()) // output chat message without any format code (like color or bold)
return nil
}

153
cmd/simpleServer/main.go Normal file
View File

@ -0,0 +1,153 @@
// Example minecraft 1.15.2 server
package main
import (
"github.com/Tnze/go-mc/bot"
"github.com/Tnze/go-mc/data"
"github.com/Tnze/go-mc/net"
pk "github.com/Tnze/go-mc/net/packet"
"github.com/google/uuid"
"log"
)
const ProtocolVersion = 578
const Threshold = 256
const MaxPlayer = 200
func main() {
l, err := net.ListenMC(":25565")
if err != nil {
log.Fatalf("Listen error: %v", err)
}
for {
conn, err := l.Accept()
if err != nil {
log.Fatalf("Accept error: %v", err)
}
go acceptConn(conn)
}
}
func acceptConn(conn net.Conn) {
defer conn.Close()
// handshake
protocol, intention, err := handshake(conn)
if err != nil {
log.Printf("Handshake error: %v", err)
return
}
switch intention {
default: //unknown error
log.Printf("Unknown handshake intention: %v", intention)
case 1: //for status
acceptListPing(conn)
case 2: //for login
handlePlaying(conn, protocol)
}
}
func handlePlaying(conn net.Conn, protocol int32) {
// login, get player info
info, err := acceptLogin(conn)
if err != nil {
log.Print("Login failed")
return
}
// Write LoginSuccess packet
err = loginSuccess(conn, info.Name, info.UUID)
if err != nil {
log.Print("Login failed on success")
return
}
joinGame(conn)
conn.WritePacket(pk.Marshal(data.PlayerPositionAndLookClientbound,
// https://wiki.vg/Protocol#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
))
// Just for block this goroutine. Keep the connection
for {
if _, err := conn.ReadPacket(); err != nil {
log.Printf("ReadPacket error: %v", err)
break
}
// KeepAlive packet is not handled, so client might
// exit because of "time out".
}
}
type PlayerInfo struct {
Name string
UUID uuid.UUID
OPLevel int
}
// acceptLogin check player's account
func acceptLogin(conn net.Conn) (info PlayerInfo, err error) {
//login start
var p pk.Packet
p, err = conn.ReadPacket()
if err != nil {
return
}
err = p.Scan((*pk.String)(&info.Name)) //decode username as pk.String
if err != nil {
return
}
//auth
const OnlineMode = false
if OnlineMode {
log.Panic("Not Implement")
} else {
// offline-mode UUID
info.UUID = bot.OfflineUUID(info.Name)
}
return
}
// handshake receive and parse Handshake packet
func handshake(conn net.Conn) (protocol, intention int32, err error) {
var (
Protocol, Intention pk.VarInt
ServerAddress pk.String // ignored
ServerPort pk.UnsignedShort // ignored
)
// receive handshake packet
p, err := conn.ReadPacket()
if err != nil {
return 0, 0, err
}
err = p.Scan(&Protocol, &ServerAddress, &ServerPort, &Intention)
return int32(Protocol), int32(Intention), err
}
// loginSuccess send LoginSuccess packet to client
func loginSuccess(conn net.Conn, name string, uuid uuid.UUID) error {
return conn.WritePacket(pk.Marshal(0x02,
pk.String(uuid.String()), //uuid as string with hyphens
pk.String(name),
))
}
func joinGame(conn net.Conn) error {
return conn.WritePacket(pk.Marshal(data.JoinGame,
pk.Int(0), // EntityID
pk.UnsignedByte(1), // Gamemode
pk.Int(0), // Dimension
pk.Long(0), // HashedSeed
pk.UnsignedByte(MaxPlayer), // MaxPlayer
pk.String("default"), // LevelType
pk.VarInt(15), // View Distance
pk.Boolean(false), // Reduced Debug Info
pk.Boolean(true), // Enable respawn screen
))
}

View File

@ -0,0 +1,64 @@
package main
import (
"encoding/json"
"github.com/Tnze/go-mc/chat"
"github.com/Tnze/go-mc/net"
pk "github.com/Tnze/go-mc/net/packet"
"github.com/google/uuid"
"log"
)
func acceptListPing(conn net.Conn) {
for i := 0; i < 2; i++ { // ping or list. Only accept twice
p, err := conn.ReadPacket()
if err != nil {
return
}
switch p.ID {
case 0x00: //List
err = conn.WritePacket(pk.Marshal(0x00, pk.String(listResp())))
case 0x01: //Ping
err = conn.WritePacket(p)
}
if err != nil {
return
}
}
}
type player struct {
Name string `json:"name"`
ID uuid.UUID `json:"id"`
}
// listResp return server status as JSON string
func listResp() string {
var list struct {
Version struct {
Name string `json:"name"`
Protocol int `json:"protocol"`
} `json:"version"`
Players struct {
Max int `json:"max"`
Online int `json:"online"`
Sample []player `json:"sample"`
} `json:"players"`
Description chat.Message `json:"description"`
FavIcon string `json:"favicon,omitempty"`
}
list.Version.Name = "Chat Server"
list.Version.Protocol = ProtocolVersion
list.Players.Max = MaxPlayer
list.Players.Online = 123
list.Players.Sample = []player{} // must init. can't be nil
list.Description = chat.Message{Text: "Powered by go-mc", Color: "blue"}
data, err := json.Marshal(list)
if err != nil {
log.Panic("Marshal JSON for status checking fail")
}
return string(data)
}

View File

@ -4,26 +4,25 @@ package data
const (
SpawnObject int32 = iota //0x00
SpawnExperienceOrb
SpawnGlobalEntity
SpawnMob
SpawnLivingEntity
SpawnPainting
SpawnPlayer
AnimationClientbound
EntityAnimationClientbound
Statistics
AcknowledgePlayerDigging
BlockBreakAnimation
UpdateBlockEntity
BlockEntityData
BlockAction
BlockChange
BossBar
ServerDifficulty
ChatMessageClientbound
MultiBlockChange
MultiBlockChange //0x10
TabComplete
TabComplete //0x10
DeclareCommands
ConfirmTransaction
CloseWindow
WindowConfirmationClientbound
CloseWindowClientbound
WindowItems
WindowProperty
SetSlot
@ -35,9 +34,9 @@ const (
Explosion
UnloadChunk
ChangeGameState
OpenHorseWindow
OpenHorseWindow //0x20
KeepAliveClientbound
KeepAliveClientbound //0x20
ChunkData
Effect
Particle
@ -52,9 +51,9 @@ const (
VehicleMoveClientbound
OpenBook
OpenWindow
OpenSignEditor
OpenSignEditor //0x30
CraftRecipeResponse
CraftRecipeResponse //0x30
PlayerAbilitiesClientbound
CombatEvent
PlayerInfo
@ -69,10 +68,11 @@ const (
SelectAdvancementTab
WorldBorder
Camera
HeldItemChangeClientbound
HeldItemChangeClientbound //0x40
UpdateViewPosition
UpdateViewPosition //0x40
UpdateViewDistance
SpawnPosition
DisplayScoreboard
EntityMetadata
AttachEntity
@ -84,11 +84,10 @@ const (
SetPassengers
Teams
UpdateScore
SpawnPosition
TimeUpdate
Title
Title //0x50
EntitySoundEffect
EntitySoundEffect //0x50
SoundEffect
StopSound
PlayerListHeaderAndFooter
@ -99,7 +98,7 @@ const (
EntityProperties
EntityEffect
DeclareRecipes
Tags //0x5C
Tags //0x5B
)
// Serverbound packet IDs
@ -119,9 +118,10 @@ const (
EditBook
QueryEntityNBT
UseEntity
KeepAliveServerbound
GenerateStructure
LockDifficulty //0x10
KeepAliveServerbound //0x10
LockDifficulty
PlayerPosition
PlayerPositionAndLookServerbound
PlayerLook
@ -136,9 +136,9 @@ const (
SteerVehicle
RecipeBookData
NameItem
ResourcePackStatus
AdvancementTab //0x20
ResourcePackStatus //0x20
AdvancementTab
SelectTrade
SetBeaconEffect
HeldItemChangeServerbound
@ -151,5 +151,5 @@ const (
AnimationServerbound
Spectate
PlayerBlockPlacement
UseItem //0x2D
UseItem //0x2E
)