github.com/metaworking/channeld@v0.7.3/pkg/channeld/entity.go (about) 1 package channeld 2 3 import ( 4 "fmt" 5 "sync" 6 7 "github.com/metaworking/channeld/pkg/channeldpb" 8 "github.com/metaworking/channeld/pkg/common" 9 "github.com/puzpuzpuz/xsync/v2" 10 "go.uber.org/zap" 11 ) 12 13 type EntityId uint32 14 15 // type EntityChannel struct { 16 // *Channel 17 // } 18 19 type EntityGroup struct { 20 entityIds *xsync.MapOf[EntityId, interface{}] //[]EntityId 21 } 22 23 func newEntityGroup() *EntityGroup { 24 return &EntityGroup{ 25 entityIds: xsync.NewTypedMapOf[EntityId, interface{}](UintIdHasher[EntityId]()), 26 } 27 } 28 29 func (g *EntityGroup) Add(groupToAdd *EntityGroup) { 30 if groupToAdd == nil { 31 return 32 } 33 34 groupToAdd.entityIds.Range(func(key EntityId, value interface{}) bool { 35 g.entityIds.Store(key, value) 36 return true 37 }) 38 } 39 40 func toArray[T UintId](g *EntityGroup) []T { 41 arr := []T{} 42 g.entityIds.Range(func(key EntityId, value interface{}) bool { 43 arr = append(arr, T(key)) 44 return true 45 }) 46 return arr 47 } 48 49 type EntityGroupController interface { 50 Initialize(ch *Channel) 51 Uninitialize(ch *Channel) 52 cascadeGroup(t channeldpb.EntityGroupType, group *EntityGroup) 53 AddToGroup(t channeldpb.EntityGroupType, entitiesToAdd []EntityId) error 54 RemoveFromGroup(t channeldpb.EntityGroupType, entitiesToRemove []EntityId) error 55 GetHandoverEntities() []EntityId 56 } 57 58 // FlatEntityGroupController is a simple implementation of EntityGroupController which has 59 // only one layer of handover and lock group. Adding an entities in a group to another group 60 // will overwrite the previous group and the overwrite is not revertible. 61 type FlatEntityGroupController struct { 62 entityId EntityId 63 rwLock sync.RWMutex 64 handoverGroup *EntityGroup 65 lockGroup *EntityGroup 66 } 67 68 func (ctl *FlatEntityGroupController) Initialize(ch *Channel) { 69 ctl.entityId = EntityId(ch.Id()) 70 } 71 72 func (ctl *FlatEntityGroupController) Uninitialize(ch *Channel) { 73 if ch.Type() != channeldpb.ChannelType_ENTITY { 74 return 75 } 76 77 // Remove the entity itself from its current groups (which may be shared between multiple entity channels) 78 ctl.RemoveFromGroup(channeldpb.EntityGroupType_HANDOVER, []EntityId{ctl.entityId}) 79 ctl.RemoveFromGroup(channeldpb.EntityGroupType_LOCK, []EntityId{ctl.entityId}) 80 } 81 82 func (ctl *FlatEntityGroupController) cascadeGroup(t channeldpb.EntityGroupType, group *EntityGroup) { 83 // Current entity is already locked, won't cascade. 84 if ctl.lockGroup != nil && ctl.lockGroup.entityIds.Size() > 0 { 85 return 86 } 87 88 if t == channeldpb.EntityGroupType_HANDOVER { 89 // Add the entities in current group to the new group 90 group.Add(ctl.handoverGroup) 91 92 // Set current group to the new group 93 ctl.handoverGroup = group 94 } else if t == channeldpb.EntityGroupType_LOCK { 95 // LOCK has higher priority than HANDOVER, so the cascade brings the entities in the handover group into the lock group 96 group.Add(ctl.handoverGroup) 97 group.Add(ctl.lockGroup) 98 99 // Set current group to the new group 100 ctl.lockGroup = group 101 } 102 } 103 104 func (ctl *FlatEntityGroupController) AddToGroup(t channeldpb.EntityGroupType, entitiesToAdd []EntityId) error { 105 ctl.rwLock.Lock() 106 defer ctl.rwLock.Unlock() 107 108 if t == channeldpb.EntityGroupType_HANDOVER { 109 if ctl.handoverGroup == nil { 110 ctl.handoverGroup = newEntityGroup() 111 } 112 113 for _, entityId := range entitiesToAdd { 114 ctl.handoverGroup.entityIds.Store(entityId, nil) 115 116 ch := GetChannel(common.ChannelId(entityId)) 117 if ch == nil { 118 continue 119 } 120 121 if ch.entityController == nil { 122 ch.Logger().Error("channel doesn't have the entity controller") 123 continue 124 } 125 126 // All entity channels of the same group share the same handover group instance 127 ch.entityController.cascadeGroup(t, ctl.handoverGroup) 128 } 129 130 rootLogger.Debug("updated handover group", zap.Uint32("entityId", uint32(ctl.entityId)), 131 zap.Any("handoverGroup", toArray[EntityId](ctl.handoverGroup))) 132 133 } else if t == channeldpb.EntityGroupType_LOCK { 134 if ctl.lockGroup == nil { 135 ctl.lockGroup = newEntityGroup() 136 } 137 138 for _, entityId := range entitiesToAdd { 139 ctl.lockGroup.entityIds.Store(entityId, nil) 140 141 ch := GetChannel(common.ChannelId(entityId)) 142 if ch == nil { 143 continue 144 } 145 146 if ch.entityController == nil { 147 ch.Logger().Error("channel doesn't have the entity controller") 148 continue 149 } 150 151 // All entity channels of the same group share the same lock group instance 152 ch.entityController.cascadeGroup(t, ctl.lockGroup) 153 } 154 155 rootLogger.Debug("updated lock group", zap.Uint32("entityId", uint32(ctl.entityId)), 156 zap.Any("lockGroup", toArray[EntityId](ctl.lockGroup))) 157 } 158 159 return nil 160 } 161 162 func (ctl *FlatEntityGroupController) RemoveFromGroup(t channeldpb.EntityGroupType, entitiesToRemove []EntityId) error { 163 ctl.rwLock.Lock() 164 defer ctl.rwLock.Unlock() 165 166 if t == channeldpb.EntityGroupType_HANDOVER { 167 if ctl.handoverGroup != nil { 168 for _, entityId := range entitiesToRemove { 169 ctl.handoverGroup.entityIds.Delete(entityId) 170 // Reset the removed entity's entity channel's handover group 171 entityCh := GetChannel(common.ChannelId(entityId)) 172 if entityCh != nil { 173 entityCh.entityController.(*FlatEntityGroupController).handoverGroup = newEntityGroup() 174 } 175 } 176 } else { 177 return fmt.Errorf("handover group is nil, entityId: %d", ctl.entityId) 178 } 179 } else if t == channeldpb.EntityGroupType_LOCK { 180 if ctl.lockGroup != nil { 181 for _, entityId := range entitiesToRemove { 182 ctl.lockGroup.entityIds.Delete(entityId) 183 // Reset the removed entity's entity channel's lock group 184 entityCh := GetChannel(common.ChannelId(entityId)) 185 if entityCh != nil { 186 entityCh.entityController.(*FlatEntityGroupController).lockGroup = newEntityGroup() 187 } 188 } 189 } else { 190 return fmt.Errorf("lock group is nil, entityId: %d", ctl.entityId) 191 } 192 } 193 194 return nil 195 } 196 197 func (ctl *FlatEntityGroupController) GetHandoverEntities() []EntityId { 198 // If AddToGroup is never called, return the entity itself 199 if ctl.handoverGroup == nil { 200 return []EntityId{ctl.entityId} 201 } 202 203 ctl.rwLock.RLock() 204 defer ctl.rwLock.RUnlock() 205 206 arr := make([]EntityId, 0, ctl.handoverGroup.entityIds.Size()) 207 locked := false 208 ctl.handoverGroup.entityIds.Range(func(key EntityId, _ interface{}) bool { 209 if ctl.lockGroup != nil { 210 // If any entity in the handover group is locked, the handover should not happen. 211 if _, locked = ctl.lockGroup.entityIds.Load(key); locked { 212 return false 213 } 214 } 215 arr = append(arr, key) 216 return true 217 }) 218 219 if locked { 220 return []EntityId{} 221 } 222 223 return arr 224 } 225 226 func (ch *Channel) GetHandoverEntities(notifyingEntityId EntityId) map[EntityId]common.Message { 227 if ch.entityController == nil { 228 ch.Logger().Error("channel doesn't have the entity controller") 229 return nil 230 } 231 232 entityIds := ch.entityController.GetHandoverEntities() 233 entities := make(map[EntityId]common.Message, len(entityIds)) 234 for _, entityId := range entityIds { 235 entityChannel := GetChannel(common.ChannelId(entityId)) 236 if entityChannel == nil { 237 entities[entityId] = nil 238 continue 239 } 240 entities[entityId] = entityChannel.GetDataMessage() 241 } 242 243 return entities 244 } 245 246 func handleAddEntityGroup(ctx MessageContext) { 247 if ctx.Connection != ctx.Channel.GetOwner() { 248 ctx.Connection.Logger().Error("AddEntityGroupMessage should only handled for the owner connection of the entity channel") 249 return 250 } 251 252 addMsg, ok := ctx.Msg.(*channeldpb.AddEntityGroupMessage) 253 if !ok { 254 ctx.Connection.Logger().Error("message is not an AddEntityGroupMessage, will not be handled.") 255 return 256 } 257 258 if ctx.Channel.entityController == nil { 259 ctx.Channel.Logger().Error("channel doesn't have the entity controller") 260 return 261 } 262 263 if ctx.Channel.entityController.AddToGroup(addMsg.Type, CopyArray[uint32, EntityId](addMsg.EntitiesToAdd)) != nil { 264 ctx.Channel.Logger().Error("failed to add entities to group", 265 zap.Int32("groupType", int32(addMsg.Type)), 266 zap.Uint32s("entitiesToAdd", addMsg.EntitiesToAdd), 267 ) 268 } 269 } 270 271 func handleRemoveEntityGroup(ctx MessageContext) { 272 if ctx.Connection != ctx.Channel.GetOwner() { 273 ctx.Connection.Logger().Error("RemoveEntityGroupMessage should only handled for the owner connection of the entity channel") 274 return 275 } 276 277 removeMsg, ok := ctx.Msg.(*channeldpb.RemoveEntityGroupMessage) 278 if !ok { 279 ctx.Connection.Logger().Error("message is not an RemoveEntityGroupMessage, will not be handled.") 280 return 281 } 282 283 if ctx.Channel.entityController == nil { 284 ctx.Channel.Logger().Error("channel doesn't have the entity controller") 285 return 286 } 287 288 if ctx.Channel.entityController.RemoveFromGroup(removeMsg.Type, CopyArray[uint32, EntityId](removeMsg.EntitiesToRemove)) != nil { 289 ctx.Channel.Logger().Error("failed to remove entities from group", 290 zap.Int32("groupType", int32(removeMsg.Type)), 291 zap.Uint32s("entitiesToRemove", removeMsg.EntitiesToRemove), 292 ) 293 } 294 }