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 }