Files
Ananta-ps/pkg/protocol/codecs.go
T

285 lines
7.8 KiB
Go
Raw Normal View History

package protocol
import (
"encoding/json"
"fmt"
"os"
"time"
)
// EncodeServerTime encodes the ServerTime payload.
// Format (Inline): [F64 clientUnixTime] [F64 serverUnixTime]
func EncodeServerTime(clientUnixTime float64) []byte {
now := float64(time.Now().UnixNano()) / 1e9
if clientUnixTime <= 0 {
clientUnixTime = now
}
w := NewUxWriter()
w.F64(clientUnixTime)
w.F64(now)
return w.Bytes()
}
// EncodePatchCheck encodes PatchCheckResult.
// Format (Inline): [I32 patchVersion (0)] [0xFF marker] [Int7 count (0)]
func EncodePatchCheck() []byte {
w := NewUxWriter()
w.I32(0) // patchVersion
w.U8(0xFF) // list marker
w.Int7(0) // empty list
return w.Bytes()
}
// EncodeEmptyList encodes an empty List<uint>.
func EncodeEmptyList() []byte {
w := NewUxWriter()
w.U8(0xFF)
w.Int7(0)
return w.Bytes()
}
// EncodeCheckAccount encodes CheckAccountResult.
func EncodeCheckAccount(accountId, userName, token, displayName string, aid, serverId int, pid uint64) []byte {
loginData := map[string]interface{}{
"code": 200,
"subcode": 0,
"msg": "ok",
"uid": accountId,
"sdkuid": accountId,
"user_id": accountId,
"aid": aid,
"pid": pid,
"player_id": fmt.Sprintf("%d", pid),
"role_id": fmt.Sprintf("%d", pid),
"role_name": displayName,
"server_id": fmt.Sprintf("%d", serverId),
"host_id": serverId,
"username": userName,
"account": accountId,
"platform": "pc",
"token": token,
"access_token": token,
"ext_access_token": token,
"sessionid": token,
"login_ticket": token,
"realname_status": 1,
"realname_verify_status": 1,
"mobile_bind_status": 1,
}
loginJSON, _ := json.Marshal(loginData)
w := NewUxWriter()
w.U8(0xFF) // Complex object marker
w.UxString(string(loginJSON))
w.UxString(token)
w.UxString(userName)
w.I32(int32(aid))
w.Bool(false) // NeedRealNameTip
w.Bool(false) // NeedRoleEnter
w.Bool(true) // RealNameVerified
w.I32(int32(serverId))
w.I32(200) // code
w.I32(0) // subcode
w.UxString("ok")
return w.Bytes()
}
// EncodeEnterGame encodes EnterGameResult.
func EncodeEnterGame(aid int, pid uint64, host string, port int, token string, serverId int, accountId string) []byte {
w := NewUxWriter()
w.I32(int32(aid))
w.U64(pid)
// GateServerInfo
w.U8(0xFF)
w.I32(int32(aid))
w.U64(pid)
w.UxString(host)
w.I32(int32(port))
w.UxString(token)
w.I32(int32(serverId))
w.UxString(accountId)
return w.Bytes()
}
// EncodeGameServerInfo encodes GameServerInfo.
func EncodeGameServerInfo(host string, port int, gameToken string) []byte {
w := NewUxWriter()
w.U8(0xFF)
w.UxString(host)
w.I32(int32(port))
w.UxString(gameToken)
return w.Bytes()
}
// EncodeEnterScene encodes EnterSceneInfo.
func EncodeEnterScene(pid uint64, raidId uint32, instanceId uint64, universeId uint32, unitId uint64, templateId uint32, posX, posY, posZ, facing float32) []byte {
w := NewUxWriter()
w.U8(0xFF) // [UxContract]
w.U64(pid)
w.U32(raidId)
w.U64(instanceId)
// UXVector3 Position (Struct)
w.F32(posX)
w.F32(posY)
w.F32(posZ)
w.F32(facing)
// SceneSpoonNames (empty list)
w.U8(0xFF).Int7(0)
// SceneSpoonMd5s (empty list)
w.U8(0xFF).Int7(0)
// Spirits List (count 1, Struct)
w.U8(0xFF).Int7(1)
// SpiritInitData (Struct: Id, TemplateId, IsActive, WeaponTemplateId, WeaponSkinId)
w.U64(unitId).U32(templateId).Bool(true).U32(0).U32(0)
// GridInfo (Struct: MinX, MinZ, MaxX, MaxZ)
w.I32(0).I32(0).I32(300000).I32(300000)
w.U32(0) // MatchGameId
w.U32(0) // SwitchShowId
w.Bool(false) // IsSwitchSpiritShow
w.U32(universeId)
w.U32(0) // SectorControlId
// SectorControlIds (empty list)
w.U8(0xFF).Int7(0)
w.U32(0) // DynamicLayerId
// LoadingType (TypeMark 1: byte Type, Int32 Members list, Ripple)
w.U8(1).U8(0).U8(0xFF).I32(0).Bool(false)
// LinkSimpleInfo (TypeMark 1: U64 Id, three bytes, U32 event/time, U64 host)
w.U8(1).U64(0).U8(0).U8(1).U8(0).U32(0).U32(0).U64(0)
// DataLayerIndices (empty list)
w.U8(0xFF).Int7(0)
w.Bool(false) // Seamless
// CollectionRoomLoadData (null complex)
w.U8(0x00)
// HouseLoadData (null complex)
w.U8(0x00)
// TombIslandIds (empty list)
w.U8(0xFF).Int7(0)
return w.Bytes()
}
// EncodeLogicAgentEnter encodes SyncLogicAgentEnter.
func EncodeLogicAgentEnter(unitId uint64) []byte {
w := NewUxWriter()
w.U64(unitId)
return w.Bytes()
}
// EncodeLogicAgentLeave encodes SyncLogicAgentLeave.
func EncodeLogicAgentLeave(unitId uint64) []byte {
w := NewUxWriter()
w.U64(unitId)
return w.Bytes()
}
// EncodeManagedLogicAgent encodes SyncManagedLogicAgent.
func EncodeManagedLogicAgent(unitId, pid uint64, moveId byte) []byte {
w := NewUxWriter()
w.U64(unitId).U64(pid).U8(moveId)
return w.Bytes()
}
// EncodePlayerCurrentSpirit encodes SyncPlayerCurrentSpirit.
func EncodePlayerCurrentSpirit(pid uint64, templateId uint32, unitId uint64, isAgentSwitch bool) []byte {
w := NewUxWriter()
w.U64(pid).U32(templateId).U64(unitId).Bool(isAgentSwitch)
return w.Bytes()
}
// EncodeSceneLoadCompleted encodes SyncSceneLoadCompleted.
func EncodeSceneLoadCompleted(sceneId uint64) []byte {
w := NewUxWriter()
w.U64(sceneId)
return w.Bytes()
}
// EncodeRaidBattleUnitSpirit encodes the character projection for world entry and spirit switching.
func EncodeRaidBattleUnitSpirit(unitId uint64, templateId uint32, posX, posY, posZ, facing float32, pid uint64) []byte {
w := NewUxWriter()
w.U8(0xFF) // RaidBattleUnitSpirit Complex marker
w.U8(0xFF) // SpiritWearFashionsInfo Complex marker
// WearFashionColoringInfoDict empty dictionary (0xFF marker, count 0)
w.U8(0xFF).I32(0)
// WearFashionInfoList with 5 default fashion items
w.U8(0xFF).I32(5)
fashionIds := []uint32{11100051, 11100052, 11100053, 11189102, 11189103}
for _, fid := range fashionIds {
w.U8(0xFF).U32(fid)
}
// WearFashionEditInfoList empty list
w.U8(0xFF).I32(0)
// HiddenParts = 0, EditedHiddenParts = 0, Begging = false, IsBot = false
w.U8(0).U8(0).Bool(false).Bool(false)
// Id & TemplateId
w.U64(unitId).U32(templateId)
// Position & Facing
w.F32(posX).F32(posY).F32(posZ).F32(facing)
// OwnerId & ManagedPid
w.U64(pid).U64(0)
// MoveId & NavTags
w.U8(0).U32(0)
// GroundData (MoveActionGroundData Complex 0xFF)
w.U8(0xFF).U8(0).U64(0).U8(0)
// LocalPos (UXVector3)
w.F32(0).F32(0).F32(0)
return w.Bytes()
}
// EncodePlayerInfo loads the baseline PlayerClientInfo and patches dynamic fields.
func EncodePlayerInfo(templatePath string, aid int, pid uint64, accountId, name string) ([]byte, error) {
data, err := os.ReadFile(templatePath)
if err != nil {
return nil, err
}
// PlayerClientInfo: complex marker, Config buffer (Int7), InfoLogin marker,
// Aid, Pid, AccountId, Name. String lengths may change; preserve the rest verbatim.
r := NewUxReader(data)
mark, err := r.U8()
if err != nil || mark != 0xff {
return nil, fmt.Errorf("invalid PlayerClientInfo marker")
}
mark, err = r.U8()
if err != nil || mark != 0xff {
return nil, fmt.Errorf("invalid Config buffer marker")
}
n, err := r.Int7()
if err != nil {
return nil, err
}
if _, err = r.Bytes(n); err != nil {
return nil, err
}
mark, err = r.U8()
if err != nil || mark != 0xff {
return nil, fmt.Errorf("invalid InfoLogin marker")
}
prefixEnd := r.Position()
if _, err = r.I32(); err != nil {
return nil, err
}
if _, err = r.U64(); err != nil {
return nil, err
}
if _, err = r.UxString(); err != nil {
return nil, err
}
if _, err = r.UxString(); err != nil {
return nil, err
}
w := NewUxWriter()
w.Raw(data[:prefixEnd]).I32(int32(aid)).U64(pid).UxString(accountId).UxString(name)
w.Raw(data[r.Position():])
return w.Bytes(), nil
}