This repository has been archived on 2026-10-31. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
Ananta-ps/pkg/protocol/codecs.go
T

261 lines
7.0 KiB
Go

package protocol
import (
"encoding/binary"
"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 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": "辰宿",
"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 (Complex 0xFF: Type, Members list, Ripple)
w.U8(0xFF).U32(0).U8(0xFF).Int7(0).Bool(false)
// LinkSimpleInfo (Complex 0xFF: Id, Mode, DeviceLevel, Tag, PublicEventId, LastPublicEventTime, HostPid)
w.U8(0xFF).U32(0).U32(0).U32(1).U32(0).U32(0).U64(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).U32(0).U64(0).U32(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
}
res := make([]byte, len(data))
copy(res, data)
// Patch Aid at offset 4 (4 bytes)
if len(res) > 8 {
binary.LittleEndian.PutUint32(res[4:8], uint32(aid))
}
// Patch Pid at offset 8 (8 bytes)
if len(res) > 16 {
binary.LittleEndian.PutUint64(res[8:16], pid)
}
return res, nil
}