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  }