close

開始之前先講明需求:

1.將收到的Packet(rawdata)轉換成可用的遊戲協議(Protocol)

2.動態生成而非使用type assertion去一個個switch case生成

3.有統一的abstract class來轉型並呼叫共通的function

 

這裡碰到了一些問題,

1.因為Golang不像C#等高階語言可以透過Reflection去走訪所有的Class(Golang也沒有Class),甚至像PHP那樣可以直接使用字串去new class,所以像C/C++那樣先行註冊所有Protocol似乎很難避免。

2.Golang沒有繼承的概念,最多就是可以宣告interface,而由其他struct去實作該interface所描述的函式。

 

下面是到處爬文跟參考後最終的結果,這作法老實說不知道是不是對的,但起碼是運作正常啦.....

package proto

import (
    "reflect"

    util "../lib/utility"
)

//宣告基礎介面類別,統一所有Protocols需要具備的函式
type IProtocol interface {
    FromBytes(*util.BinaryReader)
    ToBytes(*util.BinaryWriter)
    Todo()
}

//以名稱來存放所有註冊的Protocol實體
var protoFactory = make(map[string]IProtocol)

//初始化Protocol
func InitProtocol() {
    RegistProtocol(new(HeartBeatProto))
}

//註冊所有使用的Protocol
func RegistProtocol(dummy IProtocol) {
    dummyType := reflect.TypeOf(dummy).Elem()
    protoFactory[dummyType.Name()] = dummy
}

//由Protocol名稱產生一個對應新的Protocol
func MakeInstance(name string) IProtocol {
    if dummy, ok := protoFactory[name]; ok {
        t := reflect.New(reflect.TypeOf(dummy).Elem()).Interface()
        if instance, ok := t.(IProtocol); ok {
            return instance
        }
    }
    return nil
}

//將從Packet裡的payload轉化成遊戲使用的Protocol
func TransferRawDataToProtocol(rawData []byte) []IProtocol {

    reader := util.NewBinaryReader(rawData)
    counts := reader.ReadInt()
    protos := make([]IProtocol, counts)

    for i := 0; i < counts; i++ {
        name := reader.ReadString()
        clone := MakeInstance(name)
        clone.FromBytes(reader)

        protos[i] = clone
    }

    return protos
}

//將遊戲使用的Protocol轉成Packet內的payload
func TransferProtocolToRawData(protos []IProtocol) []byte {

    writer := util.NewBinaryWriter()
    writer.WriteInt(len(protos))

    for _, proto := range protos {
        name := reflect.TypeOf(proto).Elem().Name()
        writer.WriteString(name)
        proto.ToBytes(writer)
    }

    rawData := writer.ToBytes()

    return rawData
}

//測試用的HeartBeat Protocol
type HeartBeatProto struct {
    _id   int
    _name string
}

//從payload取得自己的資料
func (p *HeartBeatProto) FromBytes(reader *util.BinaryReader) {
    p._id = reader.ReadInt()
    p._name = reader.ReadString()
}

//將自身資料吐給payload
func (p *HeartBeatProto) ToBytes(writer *util.BinaryWriter) {
    writer.WriteInt(p._id)
    writer.WriteString(p._name)
}

//遊戲內對應Protocol真正的實作內容
func (p *HeartBeatProto) Todo() {
    //fmt.Println("HeartBeatProto Todo")
}

 

下一篇:

[Golang] 學習筆記(4) XORM設定補充

 

arrow
arrow
    文章標籤
    golang protocol reflection
    全站熱搜

    不來嗯 發表在 痞客邦 留言(0) 人氣()