65 lines
2.2 KiB
Go
65 lines
2.2 KiB
Go
package gameserver
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"net"
|
|
|
|
pb "gac-hkrpg-go/protocol/proto"
|
|
"google.golang.org/protobuf/proto"
|
|
"google.golang.org/protobuf/reflect/protoreflect"
|
|
)
|
|
|
|
type cwPacketSendError struct{ cause error }
|
|
|
|
func (e *cwPacketSendError) Error() string { return e.cause.Error() }
|
|
func (e *cwPacketSendError) Unwrap() error { return e.cause }
|
|
|
|
// Mark every explicit send failure, including serialization/projection errors,
|
|
// so the request wrapper never attempts a second response after such a failure.
|
|
func cwSend(s *Session, message proto.Message) error {
|
|
if err := s.Send(message); err != nil {
|
|
return &cwPacketSendError{cause: err}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// CW transactions report configuration/validation/save failures through their
|
|
// typed response. Returning these errors directly to the general dispatcher
|
|
// suppresses the response altogether and leaves the Client's wait block open.
|
|
// This does not manufacture success or retry a transaction after a failed save.
|
|
func cwHandle[ReqT any, RspT any](name string, fn func(*Session, *ReqT, *RspT) error) {
|
|
cwHandleNamed(name, name+"ScRsp", fn)
|
|
}
|
|
|
|
func cwHandleNamed[ReqT any, RspT any](name, responseName string, fn func(*Session, *ReqT, *RspT) error) {
|
|
handleNamed(name, responseName, func(s *Session, req *ReqT, rsp *RspT) error {
|
|
err := fn(s, req, rsp)
|
|
if err == nil || errors.Is(err, errResponseAlreadySent) {
|
|
return err
|
|
}
|
|
// An actual transport failure belongs to the connection, not a new CW
|
|
// result packet. Preserve it for the network caller to handle.
|
|
var networkError net.Error
|
|
var sendError *cwPacketSendError
|
|
if errors.As(err, &sendError) || errors.As(err, &networkError) || errors.Is(err, io.EOF) || errors.Is(err, net.ErrClosed) {
|
|
return err
|
|
}
|
|
message, ok := any(rsp).(proto.Message)
|
|
if !ok {
|
|
return err
|
|
}
|
|
view := message.ProtoReflect()
|
|
retcode := view.Descriptor().Fields().ByName("retcode")
|
|
if retcode == nil || retcode.Kind() != protoreflect.Uint32Kind {
|
|
return err
|
|
}
|
|
if view.Get(retcode).Uint() == 0 {
|
|
view.Set(retcode, protoreflect.ValueOfUint32(uint32(pb.Retcode_RET_GRID_FIGHT_CONF_MISS)))
|
|
}
|
|
PacketLog(fmt.Sprintf("CW_REQUEST failed request=%s response=%s retcode=%d error=%v", name, responseName, view.Get(retcode).Uint(), err))
|
|
return nil
|
|
})
|
|
}
|