github.com/metaworking/channeld@v0.7.3/pkg/channeld/subscription.go (about)

     1  package channeld
     2  
     3  import (
     4  	"container/list"
     5  	"errors"
     6  
     7  	"github.com/metaworking/channeld/pkg/channeldpb"
     8  	"github.com/metaworking/channeld/pkg/common"
     9  	"go.uber.org/zap"
    10  	"google.golang.org/protobuf/proto"
    11  )
    12  
    13  type ChannelSubscription struct {
    14  	options channeldpb.ChannelSubscriptionOptions
    15  	//fanOutDataMsg  Message
    16  	//lastFanOutTime time.Time
    17  	subTime       ChannelTime
    18  	fanOutElement *list.Element
    19  }
    20  
    21  func defaultSubOptions(t channeldpb.ChannelType) *channeldpb.ChannelSubscriptionOptions {
    22  	options := &channeldpb.ChannelSubscriptionOptions{
    23  		DataAccess:           Pointer(channeldpb.ChannelDataAccess_READ_ACCESS),
    24  		DataFieldMasks:       make([]string, 0),
    25  		FanOutDelayMs:        proto.Int32(GlobalSettings.GetChannelSettings(t).DefaultFanOutDelayMs),
    26  		FanOutIntervalMs:     proto.Uint32(GlobalSettings.GetChannelSettings(t).DefaultFanOutIntervalMs),
    27  		SkipSelfUpdateFanOut: proto.Bool(true),
    28  		SkipFirstFanOut:      proto.Bool(false),
    29  	}
    30  	return options
    31  }
    32  
    33  // Returns the subscription instance if successfully subscribed, and true if subscription message should be sent.
    34  func (c *Connection) SubscribeToChannel(ch *Channel, options *channeldpb.ChannelSubscriptionOptions) (*ChannelSubscription, bool) {
    35  	if c.IsClosing() {
    36  		return nil, false
    37  	}
    38  
    39  	defer func() {
    40  		ch.subLock.Unlock()
    41  	}()
    42  	ch.subLock.Lock()
    43  
    44  	cs, exists := ch.subscribedConnections[c]
    45  	if exists {
    46  		dataAccessChanged := false
    47  		if options != nil {
    48  			dataAcess := cs.options.DataAccess
    49  			c.Logger().Verbose("already subed to channel, the sub options will be merged",
    50  				zap.String("channelType", ch.channelType.String()),
    51  				zap.Uint32("channelId", uint32(ch.id)),
    52  			)
    53  			proto.Merge(&cs.options, options)
    54  			dataAccessChanged = *dataAcess != *cs.options.DataAccess
    55  		}
    56  		return cs, dataAccessChanged
    57  	}
    58  
    59  	cs = &ChannelSubscription{
    60  		options: *defaultSubOptions(ch.channelType),
    61  		// Send the whole data to the connection when subscribed
    62  		//fanOutDataMsg: ch.Data().msg,
    63  		subTime: ch.GetTime(),
    64  	}
    65  
    66  	if options != nil {
    67  		proto.Merge(&cs.options, options)
    68  	}
    69  
    70  	cs.fanOutElement = ch.fanOutQueue.PushFront(&fanOutConnection{
    71  		conn:           c,
    72  		hadFirstFanOut: *cs.options.SkipFirstFanOut,
    73  		// Delay the first fanout, to solve the spawn & update order issue in Mirror & UE.
    74  		lastFanOutTime: ch.GetTime().OffsetMs(*cs.options.FanOutDelayMs),
    75  	})
    76  	// rootLogger.Info("conn sub to channel",
    77  	// 	zap.Uint32("connId", uint32(cs.fanOutElement.Value.(*fanOutConnection).conn.Id())),
    78  	// 	zap.Int32("fanOutDelayMs", cs.options.FanOutDelayMs),
    79  	// 	zap.Int64("channelTime", int64(ch.GetTime())),
    80  	// 	zap.Int64("nextFanOutTime", int64(cs.fanOutElement.Value.(*fanOutConnection).lastFanOutTime)),
    81  	// )
    82  
    83  	// Records the maximum fan-out interval for checking if the oldest update message is removable when the buffer is overflowed.
    84  	if ch.data != nil && ch.data.maxFanOutIntervalMs < *cs.options.FanOutIntervalMs {
    85  		ch.data.maxFanOutIntervalMs = *cs.options.FanOutIntervalMs
    86  	}
    87  	ch.subscribedConnections[c] = cs
    88  
    89  	if ch.channelType == channeldpb.ChannelType_SPATIAL {
    90  		c.spatialSubscriptions.Store(ch.id, &cs.options)
    91  	}
    92  
    93  	ch.Logger().Debug("subscribed connection",
    94  		zap.Uint32("connId", uint32(c.Id())),
    95  		zap.String("dataAccess", channeldpb.ChannelDataAccess_name[int32(*cs.options.DataAccess)]),
    96  		zap.Uint32("fanOutIntervalMs", *cs.options.FanOutIntervalMs),
    97  		zap.Int32("fanOutDelayMs", *cs.options.FanOutDelayMs),
    98  		zap.Bool("skipSelfUpdateFanOut", *cs.options.SkipSelfUpdateFanOut),
    99  		zap.Bool("skipFirstFanOut", *cs.options.SkipFirstFanOut),
   100  	)
   101  	return cs, true
   102  }
   103  
   104  func (c *Connection) UnsubscribeFromChannel(ch *Channel) (*channeldpb.ChannelSubscriptionOptions, error) {
   105  	defer func() {
   106  		ch.subLock.Unlock()
   107  	}()
   108  	ch.subLock.Lock()
   109  
   110  	cs, exists := ch.subscribedConnections[c]
   111  	if !exists {
   112  		return nil, errors.New("subscription does not exist")
   113  	}
   114  
   115  	ch.fanOutQueue.Remove(cs.fanOutElement)
   116  
   117  	delete(ch.subscribedConnections, c)
   118  
   119  	if ch.channelType == channeldpb.ChannelType_SPATIAL {
   120  		c.spatialSubscriptions.Delete(ch.id)
   121  	}
   122  
   123  	ch.Logger().Debug("unsubscribed connection", zap.Uint32("connId", uint32(c.Id())))
   124  	return &cs.options, nil
   125  }
   126  
   127  /*
   128  func (c *Connection) sendConnSubscribed(connId ConnectionId, ids ...ChannelId) {
   129  	channelIds := make([]uint32, len(ids))
   130  	for i, id := range ids {
   131  		channelIds[i] = uint32(id)
   132  	}
   133  	subMsg := &channeldpb.SubscribedToChannelsMessage{ConnId: uint32(connId), ChannelIds: channelIds}
   134  	c.SendWithGlobalChannel(channeldpb.MessageType_SUB_TO_CHANNEL, subMsg)
   135  }
   136  
   137  func (c *Connection) sendConnUnsubscribed(connId ConnectionId, ids ...ChannelId) {
   138  	channelIds := make([]uint32, len(ids))
   139  	for i, id := range ids {
   140  		channelIds[i] = uint32(id)
   141  	}
   142  	subMsg := &channeldpb.UnsubscribedToChannelsMessage{ConnId: uint32(connId), ChannelIds: channelIds}
   143  	c.SendWithGlobalChannel(channeldpb.MessageType_UNSUB_FROM_CHANNEL, subMsg)
   144  }
   145  */
   146  
   147  func (c *Connection) sendSubscribed(ctx MessageContext, ch *Channel, connToSub ConnectionInChannel, stubId uint32, subOptions *channeldpb.ChannelSubscriptionOptions) {
   148  	ctx.ChannelId = uint32(ch.id)
   149  	ctx.StubId = stubId
   150  	ctx.MsgType = channeldpb.MessageType_SUB_TO_CHANNEL
   151  	ctx.Msg = &channeldpb.SubscribedToChannelResultMessage{
   152  		ConnId:      uint32(connToSub.Id()),
   153  		SubOptions:  subOptions,
   154  		ConnType:    connToSub.GetConnectionType(),
   155  		ChannelType: ch.channelType,
   156  	}
   157  	// ctx.Msg = &channeldpb.SubscribedToChannelMessage{
   158  	// 	ConnId:     uint32(ctx.Connection.id),
   159  	// 	SubOptions: &ch.subscribedConnections[c.id].options,
   160  	// }
   161  	c.Send(ctx)
   162  
   163  	c.Logger().Debug("sent SUB_TO_CHANNEL", zap.Uint32("channelId", ctx.ChannelId), zap.Uint32("connToSub", uint32(connToSub.Id())))
   164  }
   165  
   166  func (c *Connection) sendUnsubscribed(ctx MessageContext, ch *Channel, connToUnsub *Connection, stubId uint32) {
   167  	if connToUnsub == nil {
   168  		connToUnsub = c
   169  	}
   170  	ctx.ChannelId = uint32(ch.id)
   171  	ctx.StubId = stubId
   172  	ctx.MsgType = channeldpb.MessageType_UNSUB_FROM_CHANNEL
   173  	ctx.Msg = &channeldpb.UnsubscribedFromChannelResultMessage{
   174  		ConnId:      uint32(connToUnsub.id),
   175  		ConnType:    connToUnsub.connectionType,
   176  		ChannelType: ch.channelType,
   177  	}
   178  	c.Send(ctx)
   179  }
   180  
   181  func (c *Connection) HasInterestIn(spatialChId common.ChannelId) bool {
   182  	if c.spatialSubscriptions == nil {
   183  		return false
   184  	}
   185  	_, exists := c.spatialSubscriptions.Load(spatialChId)
   186  	return exists
   187  }