fix(go): repair migration wire contracts and runtime lifecycle; document incomplete port
This commit is contained in:
1 parent
e43a310617
commit
32e47014b5
35 files changed
+16826
-438
No files matched your search
+200
-122
@@ -6,8 +6,11 @@ import (
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"ananta-ps/internal/config"
|
||||
"ananta-ps/pkg/crypto/chacha8"
|
||||
@@ -20,6 +23,7 @@ type Server struct {
|
||||
ln net.Listener
|
||||
cancelFunc context.CancelFunc
|
||||
wg sync.WaitGroup
|
||||
connections map[net.Conn]struct{}
|
||||
sessions map[string]*Session
|
||||
mu sync.RWMutex
|
||||
trafficEngine *CityTrafficEngine
|
||||
@@ -28,28 +32,34 @@ type Server struct {
|
||||
}
|
||||
|
||||
type Session struct {
|
||||
id string
|
||||
conn net.Conn
|
||||
readCipher *chacha8.UxChaCha8
|
||||
sendCipher *chacha8.UxChaCha8
|
||||
sendMu sync.Mutex
|
||||
pid uint64
|
||||
unitId uint64
|
||||
templateId uint32
|
||||
posX float32
|
||||
posY float32
|
||||
posZ float32
|
||||
facing float32
|
||||
activeScene uint64
|
||||
activeRaid uint32
|
||||
universeId uint32
|
||||
lastShopExitMs int64
|
||||
pendingTeleport *PreTeleportOption
|
||||
activeVehicleId uint64
|
||||
isDriving bool
|
||||
wantedLevel uint32
|
||||
balloonScore uint32
|
||||
racing *RacingSession
|
||||
replyKind protocol.RpcPacketKind
|
||||
stateMu sync.Mutex
|
||||
ready atomic.Bool
|
||||
hangup atomic.Bool
|
||||
initialStateSent bool
|
||||
writeTimeout time.Duration
|
||||
id string
|
||||
conn net.Conn
|
||||
readCipher *chacha8.UxChaCha8
|
||||
sendCipher *chacha8.UxChaCha8
|
||||
sendMu sync.Mutex
|
||||
pid uint64
|
||||
unitId uint64
|
||||
templateId uint32
|
||||
posX float32
|
||||
posY float32
|
||||
posZ float32
|
||||
facing float32
|
||||
activeScene uint64
|
||||
activeRaid uint32
|
||||
universeId uint32
|
||||
lastShopExitMs int64
|
||||
pendingTeleport *PreTeleportOption
|
||||
activeVehicleId uint64
|
||||
isDriving bool
|
||||
wantedLevel uint32
|
||||
balloonScore uint32
|
||||
racing *RacingSession
|
||||
}
|
||||
|
||||
func New(cfg *config.Config, templatePath string) *Server {
|
||||
@@ -57,11 +67,13 @@ func New(cfg *config.Config, templatePath string) *Server {
|
||||
candidates := []string{
|
||||
"data/player_info_template.bin",
|
||||
"../data/player_info_template.bin",
|
||||
"../../data/player_info_template.bin",
|
||||
}
|
||||
for _, c := range candidates {
|
||||
abs, _ := filepath.Abs(c)
|
||||
templatePath = abs
|
||||
break
|
||||
if _, err := os.Stat(c); err == nil {
|
||||
templatePath, _ = filepath.Abs(c)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,6 +81,7 @@ func New(cfg *config.Config, templatePath string) *Server {
|
||||
cfg: cfg,
|
||||
templatePath: templatePath,
|
||||
sessions: make(map[string]*Session),
|
||||
connections: make(map[net.Conn]struct{}),
|
||||
}
|
||||
srv.trafficEngine = NewCityTrafficEngine(srv)
|
||||
srv.crowdEngine = NewUrbanCrowdEngine(srv)
|
||||
@@ -84,16 +97,15 @@ func (s *Server) Start() error {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
s.cancelFunc = cancel
|
||||
|
||||
// Load Road Network for city traffic simulation
|
||||
_ = s.trafficEngine.LoadRoadNetwork("data/client_data/World/RoadNetwork.json")
|
||||
s.trafficEngine.Start(ctx)
|
||||
|
||||
addr := fmt.Sprintf("%s:%d", s.cfg.Network.BindHost, s.cfg.Network.GamePort)
|
||||
ln, err := net.Listen("tcp", addr)
|
||||
if err != nil {
|
||||
cancel()
|
||||
return fmt.Errorf("failed to listen on game server %s: %w", addr, err)
|
||||
}
|
||||
s.ln = ln
|
||||
_ = s.trafficEngine.LoadRoadNetwork("data/client_data/World/RoadNetwork.json")
|
||||
s.trafficEngine.Start(ctx)
|
||||
log.Printf("[GAME-SERVER] Listening on %s:%d", s.cfg.Network.AdvertisedHost, s.cfg.Network.GamePort)
|
||||
|
||||
s.wg.Add(1)
|
||||
@@ -103,15 +115,20 @@ func (s *Server) Start() error {
|
||||
}
|
||||
|
||||
func (s *Server) Stop() error {
|
||||
if s.trafficEngine != nil {
|
||||
s.trafficEngine.Stop()
|
||||
}
|
||||
if s.cancelFunc != nil {
|
||||
s.cancelFunc()
|
||||
}
|
||||
if s.ln != nil {
|
||||
_ = s.ln.Close()
|
||||
}
|
||||
s.mu.Lock()
|
||||
for conn := range s.connections {
|
||||
_ = conn.Close()
|
||||
}
|
||||
s.mu.Unlock()
|
||||
if s.trafficEngine != nil {
|
||||
s.trafficEngine.Stop()
|
||||
}
|
||||
s.wg.Wait()
|
||||
return nil
|
||||
}
|
||||
@@ -140,7 +157,20 @@ func (s *Server) acceptLoop(ctx context.Context) {
|
||||
}
|
||||
}
|
||||
|
||||
go s.handleConnection(ctx, conn)
|
||||
s.mu.Lock()
|
||||
if ctx.Err() != nil {
|
||||
s.mu.Unlock()
|
||||
conn.Close()
|
||||
return
|
||||
}
|
||||
s.connections[conn] = struct{}{}
|
||||
s.wg.Add(1)
|
||||
s.mu.Unlock()
|
||||
go func() {
|
||||
defer s.wg.Done()
|
||||
defer func() { s.mu.Lock(); delete(s.connections, conn); s.mu.Unlock() }()
|
||||
s.handleConnection(ctx, conn)
|
||||
}()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -158,10 +188,29 @@ func (sess *Session) SendPacket(packet *protocol.RpcPacket) error {
|
||||
payload = protocol.BuildNotify(packet.MethodID, packet.Body)
|
||||
}
|
||||
|
||||
return protocol.WriteFrame(sess.conn, 0x09, payload, sess.sendCipher)
|
||||
return sess.writeFrameLocked(0x09, payload)
|
||||
}
|
||||
|
||||
func (sess *Session) writeFrameLocked(mode byte, payload []byte) error {
|
||||
timeout := sess.writeTimeout
|
||||
if timeout <= 0 {
|
||||
timeout = 15 * time.Second
|
||||
}
|
||||
if err := sess.conn.SetWriteDeadline(time.Now().Add(timeout)); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := protocol.WriteFrame(sess.conn, mode, payload, sess.sendCipher); err != nil {
|
||||
// The cipher has advanced; this stream cannot be reused after a failed write.
|
||||
sess.conn.Close()
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (sess *Session) ReturnEmptyOk(invokeID int32, methodID uint32) error {
|
||||
if sess.replyKind != protocol.PacketKindInvoke {
|
||||
return nil
|
||||
}
|
||||
return sess.SendPacket(&protocol.RpcPacket{
|
||||
Kind: protocol.PacketKindReturn,
|
||||
InvokeID: invokeID,
|
||||
@@ -172,6 +221,9 @@ func (sess *Session) ReturnEmptyOk(invokeID int32, methodID uint32) error {
|
||||
}
|
||||
|
||||
func (sess *Session) ReturnData(invokeID int32, methodID uint32, body []byte) error {
|
||||
if sess.replyKind != protocol.PacketKindInvoke {
|
||||
return nil
|
||||
}
|
||||
return sess.SendPacket(&protocol.RpcPacket{
|
||||
Kind: protocol.PacketKindReturn,
|
||||
InvokeID: invokeID,
|
||||
@@ -182,6 +234,9 @@ func (sess *Session) ReturnData(invokeID int32, methodID uint32, body []byte) er
|
||||
}
|
||||
|
||||
func (sess *Session) NotifyData(methodID uint32, body []byte) error {
|
||||
if methodID == 0 {
|
||||
return fmt.Errorf("unverified notification method")
|
||||
}
|
||||
return sess.SendPacket(&protocol.RpcPacket{
|
||||
Kind: protocol.PacketKindNotify,
|
||||
MethodID: methodID,
|
||||
@@ -201,20 +256,21 @@ func (s *Server) handleConnection(ctx context.Context, conn net.Conn) {
|
||||
}
|
||||
|
||||
sess := &Session{
|
||||
id: conn.RemoteAddr().String(),
|
||||
conn: conn,
|
||||
readCipher: readCipher,
|
||||
sendCipher: writeCipher,
|
||||
pid: s.cfg.Player.Pid,
|
||||
unitId: s.cfg.Player.InitialUnitId,
|
||||
templateId: s.cfg.Player.InitialSpiritTemplateId,
|
||||
posX: float32(s.cfg.World.Spawn.X),
|
||||
posY: float32(s.cfg.World.Spawn.Y),
|
||||
posZ: float32(s.cfg.World.Spawn.Z),
|
||||
facing: s.cfg.World.Facing,
|
||||
activeRaid: s.cfg.World.RaidId,
|
||||
activeScene: s.cfg.World.SceneInstanceId,
|
||||
universeId: s.cfg.World.UniverseId,
|
||||
id: conn.RemoteAddr().String(),
|
||||
writeTimeout: time.Duration(s.cfg.Network.WriteTimeoutMs) * time.Millisecond,
|
||||
conn: conn,
|
||||
readCipher: readCipher,
|
||||
sendCipher: writeCipher,
|
||||
pid: s.cfg.Player.Pid,
|
||||
unitId: s.cfg.Player.InitialUnitId,
|
||||
templateId: s.cfg.Player.InitialSpiritTemplateId,
|
||||
posX: float32(s.cfg.World.Spawn.X),
|
||||
posY: float32(s.cfg.World.Spawn.Y),
|
||||
posZ: float32(s.cfg.World.Spawn.Z),
|
||||
facing: s.cfg.World.Facing,
|
||||
activeRaid: s.cfg.World.RaidId,
|
||||
activeScene: s.cfg.World.SceneInstanceId,
|
||||
universeId: s.cfg.World.UniverseId,
|
||||
}
|
||||
|
||||
s.mu.Lock()
|
||||
@@ -243,10 +299,18 @@ func (s *Server) handleConnection(ctx context.Context, conn net.Conn) {
|
||||
}
|
||||
|
||||
if frame.Mode == 0x03 {
|
||||
_ = protocol.WriteFrame(conn, 0x03, frame.Payload, sess.sendCipher)
|
||||
sess.sendMu.Lock()
|
||||
err = sess.writeFrameLocked(0x03, frame.Payload)
|
||||
sess.sendMu.Unlock()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if frame.Mode != 0x09 {
|
||||
continue
|
||||
}
|
||||
packet := protocol.ParseRpcPacket(frame.Payload)
|
||||
if err := s.dispatch(sess, &packet); err != nil {
|
||||
log.Printf("[GAME-SERVER] Dispatch error for method %d (%s): %v",
|
||||
@@ -256,19 +320,34 @@ func (s *Server) handleConnection(ctx context.Context, conn net.Conn) {
|
||||
}
|
||||
|
||||
func (s *Server) dispatch(sess *Session, pkt *protocol.RpcPacket) error {
|
||||
if pkt.Kind != protocol.PacketKindInvoke && pkt.Kind != protocol.PacketKindNotify {
|
||||
return nil
|
||||
}
|
||||
sess.stateMu.Lock()
|
||||
defer sess.stateMu.Unlock()
|
||||
sess.replyKind = pkt.Kind
|
||||
defer func() { sess.replyKind = protocol.PacketKindUnknown }()
|
||||
switch pkt.MethodID {
|
||||
// ---------------- Login / World Entry ----------------
|
||||
case protocol.MethodLoginGame:
|
||||
if sess.initialStateSent {
|
||||
return nil
|
||||
}
|
||||
sess.initialStateSent = true
|
||||
log.Printf("[WORLD] LoginGame received, preparing world state for player %d", sess.pid)
|
||||
if err := sess.ReturnEmptyOk(pkt.InvokeID, pkt.MethodID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_ = sess.NotifyData(protocol.MethodSendServerTimeGame, protocol.EncodeServerTime(0))
|
||||
if err := sess.NotifyData(protocol.MethodSendServerTimeGame, protocol.EncodeServerTime(0)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
playerInfoBytes, err := protocol.EncodePlayerInfo(s.templatePath, s.cfg.Client.Aid, sess.pid, s.cfg.Player.AccountId, s.cfg.Player.DisplayName)
|
||||
if err == nil {
|
||||
_ = sess.NotifyData(protocol.MethodSyncPlayerInfo, playerInfoBytes)
|
||||
if err := sess.NotifyData(protocol.MethodSyncPlayerInfo, playerInfoBytes); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
log.Printf("[WORLD] Warning: player_info_template.bin load error: %v", err)
|
||||
}
|
||||
@@ -295,10 +374,16 @@ func (s *Server) dispatch(sess *Session, pkt *protocol.RpcPacket) error {
|
||||
return err
|
||||
}
|
||||
|
||||
_ = sess.NotifyData(protocol.MethodSyncLogicAgentEnter, protocol.EncodeLogicAgentEnter(sess.unitId))
|
||||
_ = sess.NotifyData(protocol.MethodSyncManagedLogicAgent, protocol.EncodeManagedLogicAgent(sess.unitId, sess.pid, 0))
|
||||
if err := sess.NotifyData(protocol.MethodSyncLogicAgentEnter, protocol.EncodeLogicAgentEnter(sess.unitId)); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := sess.NotifyData(protocol.MethodSyncManagedLogicAgent, protocol.EncodeManagedLogicAgent(sess.unitId, sess.pid, 0)); err != nil {
|
||||
return err
|
||||
}
|
||||
unitProjection := protocol.EncodeRaidBattleUnitSpirit(sess.unitId, sess.templateId, sess.posX, sess.posY, sess.posZ, sess.facing, sess.pid)
|
||||
_ = sess.NotifyData(protocol.MethodSyncRaidBattleUnitSpirit, unitProjection)
|
||||
if err := sess.NotifyData(protocol.MethodSyncRaidBattleUnitSpirit, unitProjection); err != nil {
|
||||
return err
|
||||
}
|
||||
return sess.NotifyData(protocol.MethodSyncPlayerCurrentSpirit, protocol.EncodePlayerCurrentSpirit(sess.pid, sess.templateId, sess.unitId, false))
|
||||
|
||||
case protocol.MethodAskLoadingFinished:
|
||||
@@ -307,19 +392,28 @@ func (s *Server) dispatch(sess *Session, pkt *protocol.RpcPacket) error {
|
||||
return err
|
||||
}
|
||||
|
||||
_ = sess.NotifyData(protocol.MethodSyncSceneLoadCompleted, protocol.EncodeSceneLoadCompleted(sess.activeScene))
|
||||
if err := sess.NotifyData(protocol.MethodSyncSceneLoadCompleted, protocol.EncodeSceneLoadCompleted(sess.activeScene)); err != nil {
|
||||
return err
|
||||
}
|
||||
sess.ready.Store(true)
|
||||
|
||||
wPause := protocol.NewUxWriter()
|
||||
wPause.Bool(false)
|
||||
_ = sess.NotifyData(protocol.MethodSyncGamePause, wPause.Bytes())
|
||||
if err := sess.NotifyData(protocol.MethodSyncGamePause, wPause.Bytes()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
wFog := protocol.NewUxWriter()
|
||||
wFog.U32(uint32(sess.activeScene)).Bool(true)
|
||||
_ = sess.NotifyData(protocol.MethodSyncSceneFogMapAllUnlock, wFog.Bytes())
|
||||
if err := sess.NotifyData(protocol.MethodSyncSceneFogMapAllUnlock, wFog.Bytes()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
wPower := protocol.NewUxWriter()
|
||||
wPower.U8(0xFF).Int7(1).U32(sess.templateId).F32(1000.0)
|
||||
_ = sess.NotifyData(protocol.MethodSyncAllSpiritCombatPower, wPower.Bytes())
|
||||
if err := sess.NotifyData(protocol.MethodSyncAllSpiritCombatPower, wPower.Bytes()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
wDive := protocol.NewUxWriter()
|
||||
wDive.UxString("EnableDive").U8(0xFF).Int7(1).UxString("true")
|
||||
@@ -330,32 +424,29 @@ func (s *Server) dispatch(sess *Session, pkt *protocol.RpcPacket) error {
|
||||
w.U64(sess.pid).F64(1.0)
|
||||
return sess.NotifyData(protocol.MethodSyncPlayerLoadRate, w.Bytes())
|
||||
|
||||
// ---------------- Movement & Position ----------------
|
||||
case protocol.MethodAskMoveStart, protocol.MethodAskMoveStop:
|
||||
case protocol.MethodRequestPlayerStartHangup:
|
||||
sess.hangup.Store(true)
|
||||
return sess.ReturnEmptyOk(pkt.InvokeID, pkt.MethodID)
|
||||
case protocol.MethodRequestPlayerStopHangup:
|
||||
sess.hangup.Store(false)
|
||||
return sess.ReturnEmptyOk(pkt.InvokeID, pkt.MethodID)
|
||||
case protocol.MethodAskReportLogicAgentSyncData:
|
||||
return s.handleMovementReport(sess, pkt, false)
|
||||
case protocol.MethodAskUnitMoveActionSimple:
|
||||
return s.handleMovementReport(sess, pkt, true)
|
||||
|
||||
case protocol.MethodAskUpdatePlayerPosition:
|
||||
r := protocol.NewUxReader(pkt.Body)
|
||||
if x, err := r.F32(); err == nil {
|
||||
sess.posX = x
|
||||
}
|
||||
if y, err := r.F32(); err == nil {
|
||||
sess.posY = y
|
||||
}
|
||||
if z, err := r.F32(); err == nil {
|
||||
sess.posZ = z
|
||||
}
|
||||
return sess.ReturnEmptyOk(pkt.InvokeID, pkt.MethodID)
|
||||
// ---------------- Movement & Position ----------------
|
||||
|
||||
// ---------------- Character Switching ----------------
|
||||
case protocol.MethodAskPreSwitchSpirit:
|
||||
return sess.ReturnEmptyOk(pkt.InvokeID, pkt.MethodID)
|
||||
|
||||
case protocol.MethodAskSwitchSpirit:
|
||||
r := protocol.NewUxReader(pkt.Body)
|
||||
newTemplateId := sess.templateId
|
||||
if tid, err := r.U32(); err == nil {
|
||||
newTemplateId = tid
|
||||
newTemplateId, err := r.U32()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if newTemplateId == 0 {
|
||||
return fmt.Errorf("spirit template must be nonzero")
|
||||
}
|
||||
newUnitId := sess.unitId + 1
|
||||
|
||||
@@ -364,17 +455,32 @@ func (s *Server) dispatch(sess *Session, pkt *protocol.RpcPacket) error {
|
||||
return err
|
||||
}
|
||||
|
||||
_ = sess.NotifyData(protocol.MethodSyncLogicAgentLeave, protocol.EncodeLogicAgentLeave(sess.unitId))
|
||||
_ = sess.NotifyData(protocol.MethodSyncLogicAgentEnter, protocol.EncodeLogicAgentEnter(newUnitId))
|
||||
_ = sess.NotifyData(protocol.MethodSyncManagedLogicAgent, protocol.EncodeManagedLogicAgent(newUnitId, sess.pid, 0))
|
||||
if err := sess.NotifyData(protocol.MethodSyncLogicAgentLeave, protocol.EncodeLogicAgentLeave(sess.unitId)); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := sess.NotifyData(protocol.MethodSyncPlayerCurrentSpirit, protocol.EncodePlayerCurrentSpirit(sess.pid, newTemplateId, newUnitId, true)); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := sess.NotifyData(protocol.MethodSyncLogicAgentEnter, protocol.EncodeLogicAgentEnter(newUnitId)); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := sess.NotifyData(protocol.MethodSyncManagedLogicAgent, protocol.EncodeManagedLogicAgent(newUnitId, sess.pid, 0)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
newProj := protocol.EncodeRaidBattleUnitSpirit(newUnitId, newTemplateId, sess.posX, sess.posY, sess.posZ, sess.facing, sess.pid)
|
||||
_ = sess.NotifyData(protocol.MethodSyncRaidBattleUnitSpirit, newProj)
|
||||
_ = sess.NotifyData(protocol.MethodSyncPlayerCurrentSpirit, protocol.EncodePlayerCurrentSpirit(sess.pid, newTemplateId, newUnitId, true))
|
||||
if err := sess.NotifyData(protocol.MethodSyncRaidBattleUnitSpirit, newProj); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := sess.NotifyData(protocol.MethodSyncPlayerCurrentSpirit, protocol.EncodePlayerCurrentSpirit(sess.pid, newTemplateId, newUnitId, true)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
wInSitu := protocol.NewUxWriter()
|
||||
wInSitu.U32(newTemplateId).U64(newUnitId)
|
||||
_ = sess.NotifyData(protocol.MethodSyncSwitchSpiritInSitu, wInSitu.Bytes())
|
||||
if err := sess.NotifyData(protocol.MethodSyncSwitchSpiritInSitu, wInSitu.Bytes()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sess.templateId = newTemplateId
|
||||
sess.unitId = newUnitId
|
||||
@@ -397,12 +503,6 @@ func (s *Server) dispatch(sess *Session, pkt *protocol.RpcPacket) error {
|
||||
case protocol.MethodAskSummonVehicle:
|
||||
return s.handleAskSummonVehicle(sess, pkt)
|
||||
|
||||
case protocol.MethodAskDriveVehicle:
|
||||
return s.handleAskDriveVehicle(sess, pkt)
|
||||
|
||||
case protocol.MethodAskExitVehicle:
|
||||
return s.handleAskExitVehicle(sess, pkt)
|
||||
|
||||
case protocol.MethodAskVehicleMove:
|
||||
return s.handleAskVehicleMove(sess, pkt)
|
||||
|
||||
@@ -413,20 +513,6 @@ func (s *Server) dispatch(sess *Session, pkt *protocol.RpcPacket) error {
|
||||
return s.handleAskVehicleSendGamePlaySignal(sess, pkt)
|
||||
|
||||
// ---------------- Police & Crime ----------------
|
||||
case protocol.MethodAskSetWantedLevel:
|
||||
return s.handleAskSetWantedLevel(sess, pkt)
|
||||
|
||||
case protocol.MethodAskPoliceArrest:
|
||||
return s.handleAskPoliceArrest(sess, pkt)
|
||||
|
||||
case protocol.MethodReportPoliceArrestFinish:
|
||||
return s.handleReportPoliceArrestFinish(sess, pkt)
|
||||
|
||||
case protocol.MethodAskPayFine:
|
||||
return s.handleAskPayFine(sess, pkt)
|
||||
|
||||
case protocol.MethodAskReleaseFromPrison:
|
||||
return s.handleAskReleaseFromPrison(sess, pkt)
|
||||
|
||||
// ---------------- Dialog & NPC & Crowd ----------------
|
||||
case protocol.MethodAskStartDialog:
|
||||
@@ -447,9 +533,6 @@ func (s *Server) dispatch(sess *Session, pkt *protocol.RpcPacket) error {
|
||||
case protocol.MethodAskDoNpcAction:
|
||||
return s.handleAskDoNpcAction(sess, pkt)
|
||||
|
||||
case protocol.MethodAskSetCrowdDensity:
|
||||
return s.handleAskSetCrowdDensity(sess, pkt)
|
||||
|
||||
case protocol.MethodAskPhoneAddCallRecord:
|
||||
return s.handleAskPhoneAddCallRecord(sess, pkt)
|
||||
|
||||
@@ -484,21 +567,7 @@ func (s *Server) dispatch(sess *Session, pkt *protocol.RpcPacket) error {
|
||||
case protocol.MethodAskControlPowerHoldEnemyFallGrou:
|
||||
return s.handleAskControlPowerHoldEnemyFallGrou(sess, pkt)
|
||||
|
||||
case protocol.MethodAskPlayerAttack:
|
||||
return s.handleAskPlayerAttack(sess, pkt)
|
||||
|
||||
case protocol.MethodAskPlayerCastSkill:
|
||||
return s.handleAskPlayerCastSkill(sess, pkt)
|
||||
|
||||
case protocol.MethodAskUnlockFightStyle:
|
||||
return sess.ReturnEmptyOk(pkt.InvokeID, pkt.MethodID)
|
||||
|
||||
// ---------------- Mini-games ----------------
|
||||
case protocol.MethodAskChineseChessMove:
|
||||
return s.handleAskChineseChessMove(sess, pkt)
|
||||
|
||||
case protocol.MethodAskMahjongAction:
|
||||
return s.handleAskMahjongAction(sess, pkt)
|
||||
|
||||
case protocol.MethodEnterBalloonZone:
|
||||
return s.handleEnterBalloonZone(sess, pkt)
|
||||
@@ -554,7 +623,10 @@ func (s *Server) dispatch(sess *Session, pkt *protocol.RpcPacket) error {
|
||||
default:
|
||||
// Safe neutral typed fallback for unrouted RPC invokes
|
||||
if pkt.Kind == protocol.PacketKindInvoke {
|
||||
return sess.ReturnEmptyOk(pkt.InvokeID, pkt.MethodID)
|
||||
if body, ok := protocol.DefaultReturnBody(pkt.MethodID); ok {
|
||||
return sess.ReturnData(pkt.InvokeID, pkt.MethodID, body)
|
||||
}
|
||||
return sess.SendPacket(&protocol.RpcPacket{Kind: protocol.PacketKindReturn, InvokeID: pkt.InvokeID, MethodID: pkt.MethodID, Error: -1})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -563,12 +635,18 @@ func (s *Server) dispatch(sess *Session, pkt *protocol.RpcPacket) error {
|
||||
|
||||
// Teleport moves an active session to the specified coordinates.
|
||||
func (sess *Session) Teleport(x, y, z, facing float32) error {
|
||||
sess.stateMu.Lock()
|
||||
defer sess.stateMu.Unlock()
|
||||
return sess.teleport(x, y, z, facing)
|
||||
}
|
||||
|
||||
func (sess *Session) teleport(x, y, z, facing float32) error {
|
||||
sess.posX = x
|
||||
sess.posY = y
|
||||
sess.posZ = z
|
||||
sess.facing = facing
|
||||
|
||||
w := protocol.NewUxWriter()
|
||||
w.U64(sess.unitId).F32(x).F32(y).F32(z).F32(facing).U8(0).Bool(false).U8(6) // SetPositionType.Teleport = 6
|
||||
w.U64(sess.unitId).F32(x).F32(y).F32(z).F32(facing).U8(0).Bool(false).U8(6).U8(0).U8(0) // nullable moveGroundInfo and loadingInfo
|
||||
return sess.NotifyData(protocol.MethodSyncUnitPositionAndFacing, w.Bytes())
|
||||
}
|
||||
Reference in new issue
Block a user