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 }) }