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

     1  package channeld
     2  
     3  import (
     4  	"strings"
     5  
     6  	"github.com/metaworking/channeld/pkg/channeldpb"
     7  	"github.com/metaworking/channeld/pkg/common"
     8  	"go.uber.org/zap"
     9  )
    10  
    11  // The context of a message for both sending and receiving
    12  type MessageContext struct {
    13  	MsgType channeldpb.MessageType
    14  	// The weak-typed Message object popped from the message queue
    15  	Msg       common.Message
    16  	Broadcast uint32 //channeldpb.BroadcastType
    17  	StubId    uint32
    18  	// The original channelId in the Packet, could be different from Channel.id.
    19  	// Used for both send and receive.
    20  	ChannelId uint32
    21  
    22  	// The connection that received the message. Required for BroadcastType_ALL_BUT_SENDER but not for sending.
    23  	Connection ConnectionInChannel
    24  	// The channel that handling the message. Not required for sending or broadcasting.
    25  	Channel *Channel
    26  	// Internally used for receiving
    27  	arrivalTime ChannelTime
    28  }
    29  
    30  func (ctx *MessageContext) HasConnection() bool {
    31  	conn, ok := ctx.Connection.(*Connection)
    32  	return ok && conn != nil && !conn.IsClosing()
    33  }
    34  
    35  type MessageHandlerFunc func(ctx MessageContext)
    36  type messageMapEntry struct {
    37  	msg     common.Message
    38  	handler MessageHandlerFunc
    39  }
    40  
    41  var MessageMap = map[channeldpb.MessageType]*messageMapEntry{
    42  	channeldpb.MessageType_AUTH:                {&channeldpb.AuthMessage{}, handleAuth},
    43  	channeldpb.MessageType_CREATE_CHANNEL:      {&channeldpb.CreateChannelMessage{}, handleCreateChannel},
    44  	channeldpb.MessageType_REMOVE_CHANNEL:      {&channeldpb.RemoveChannelMessage{}, handleRemoveChannel},
    45  	channeldpb.MessageType_LIST_CHANNEL:        {&channeldpb.ListChannelMessage{}, handleListChannel},
    46  	channeldpb.MessageType_SUB_TO_CHANNEL:      {&channeldpb.SubscribedToChannelMessage{}, handleSubToChannel},
    47  	channeldpb.MessageType_UNSUB_FROM_CHANNEL:  {&channeldpb.UnsubscribedFromChannelMessage{}, handleUnsubFromChannel},
    48  	channeldpb.MessageType_CHANNEL_DATA_UPDATE: {&channeldpb.ChannelDataUpdateMessage{}, handleChannelDataUpdate},
    49  	channeldpb.MessageType_DISCONNECT:          {&channeldpb.DisconnectMessage{}, handleDisconnect},
    50  	// CREATE_CHANNEL and CREATE_SPATIAL_CHANNEL shared the same message structure and handler
    51  	channeldpb.MessageType_CREATE_SPATIAL_CHANNEL:    {&channeldpb.CreateChannelMessage{}, handleCreateChannel},
    52  	channeldpb.MessageType_QUERY_SPATIAL_CHANNEL:     {&channeldpb.QuerySpatialChannelMessage{}, handleQuerySpatialChannel},
    53  	channeldpb.MessageType_DEBUG_GET_SPATIAL_REGIONS: {&channeldpb.DebugGetSpatialRegionsMessage{}, handleGetSpatialRegionsMessage},
    54  	channeldpb.MessageType_UPDATE_SPATIAL_INTEREST:   {&channeldpb.UpdateSpatialInterestMessage{}, handleUpdateSpatialInterest},
    55  	channeldpb.MessageType_CREATE_ENTITY_CHANNEL:     {&channeldpb.CreateEntityChannelMessage{}, handleCreateEntityChannel},
    56  	channeldpb.MessageType_ENTITY_GROUP_ADD:          {&channeldpb.AddEntityGroupMessage{}, handleAddEntityGroup},
    57  	channeldpb.MessageType_ENTITY_GROUP_REMOVE:       {&channeldpb.RemoveEntityGroupMessage{}, handleRemoveEntityGroup},
    58  }
    59  
    60  func RegisterMessageHandler(msgType uint32, msg common.Message, handler MessageHandlerFunc) {
    61  	MessageMap[channeldpb.MessageType(msgType)] = &messageMapEntry{msg, handler}
    62  }
    63  
    64  func handleClientToServerUserMessage(ctx MessageContext) {
    65  	msg, ok := ctx.Msg.(*channeldpb.ServerForwardMessage)
    66  	if !ok {
    67  		ctx.Connection.Logger().Error("message is not a ServerForwardMessage, will not be handled.")
    68  		return
    69  	}
    70  
    71  	var channelOwnerConnId uint32 = 0
    72  	if ownerConn := ctx.Channel.GetOwner(); ownerConn != nil && !ownerConn.IsClosing() {
    73  		ownerConn.Send(ctx)
    74  		channelOwnerConnId = uint32(ownerConn.Id())
    75  	} else if ctx.Broadcast > 0 {
    76  		if ctx.Channel.enableClientBroadcast {
    77  			ctx.Channel.Broadcast(ctx)
    78  		} else {
    79  			ctx.Connection.Logger().Error("illegal attempt to broadcast message as the channel's client broadcasting is disabled",
    80  				zap.Uint32("msgType", uint32(ctx.MsgType)),
    81  				zap.String("channelType", ctx.Channel.channelType.String()),
    82  				zap.Uint32("channelId", uint32(ctx.Channel.id)),
    83  			)
    84  			return
    85  		}
    86  	} else {
    87  		ctx.Channel.Logger().Error("channel has no owner to forward the user-space messaged",
    88  			zap.Uint32("msgType", uint32(ctx.MsgType)),
    89  			zap.Uint32("connId", uint32(ctx.Connection.Id())),
    90  		)
    91  		return
    92  	}
    93  
    94  	if len(msg.Payload) < 128 {
    95  		ctx.Connection.Logger().Verbose("forward user-space message from client to server",
    96  			zap.Uint32("msgType", uint32(ctx.MsgType)),
    97  			zap.Uint32("clientConnId", msg.ClientConnId),
    98  			zap.Uint32("channelId", uint32(ctx.Channel.id)),
    99  			zap.Uint32("channelOwner", channelOwnerConnId),
   100  			zap.Uint32("broadcastType", ctx.Broadcast),
   101  			zap.Int("payloadSize", len(msg.Payload)),
   102  		)
   103  	} else {
   104  		ctx.Connection.Logger().Debug("forward user-space message from client to server",
   105  			zap.Uint32("msgType", uint32(ctx.MsgType)),
   106  			zap.Uint32("clientConnId", msg.ClientConnId),
   107  			zap.Uint32("channelId", uint32(ctx.Channel.id)),
   108  			zap.Uint32("channelOwner", channelOwnerConnId),
   109  			zap.Uint32("broadcastType", ctx.Broadcast),
   110  			zap.Int("payloadSize", len(msg.Payload)),
   111  		)
   112  	}
   113  }
   114  
   115  func HandleServerToClientUserMessage(ctx MessageContext) {
   116  	msg, ok := ctx.Msg.(*channeldpb.ServerForwardMessage)
   117  	if !ok {
   118  		ctx.Connection.Logger().Error("message is not a ServerForwardMessage, will not be handled.")
   119  		return
   120  	}
   121  
   122  	if len(msg.Payload) < 128 {
   123  		ctx.Connection.Logger().Verbose("forward user-space message from server to client/server",
   124  			zap.Uint32("msgType", uint32(ctx.MsgType)),
   125  			zap.Uint32("clientConnId", msg.ClientConnId),
   126  			zap.Uint32("channelId", uint32(ctx.Channel.id)),
   127  			zap.Uint32("broadcastType", ctx.Broadcast),
   128  			zap.Int("payloadSize", len(msg.Payload)),
   129  		)
   130  	} else {
   131  		ctx.Connection.Logger().Debug("forward user-space message from server to client/server",
   132  			zap.Uint32("msgType", uint32(ctx.MsgType)),
   133  			zap.Uint32("clientConnId", msg.ClientConnId),
   134  			zap.Uint32("channelId", uint32(ctx.Channel.id)),
   135  			zap.Uint32("broadcastType", ctx.Broadcast),
   136  			zap.Int("payloadSize", len(msg.Payload)),
   137  		)
   138  
   139  	}
   140  
   141  	switch channeldpb.BroadcastType(ctx.Broadcast) {
   142  	case channeldpb.BroadcastType_NO_BROADCAST:
   143  		if !ctx.Channel.SendToOwner(ctx) {
   144  			ctx.Connection.Logger().Error("cannot forward the message as the channel has no owner",
   145  				zap.Uint32("msgType", uint32(ctx.MsgType)),
   146  				zap.String("channelType", ctx.Channel.channelType.String()),
   147  				zap.Uint32("channelId", uint32(ctx.Channel.id)),
   148  			)
   149  		}
   150  
   151  		/*
   152  			case channeldpb.BroadcastType_ALL, channeldpb.BroadcastType_ALL_BUT_SENDER, channeldpb.BroadcastType_ALL_BUT_OWNER,
   153  				channeldpb.BroadcastType_ALL_BUT_CLIENT, channeldpb.BroadcastType_ALL_BUT_SERVER:
   154  				ctx.Channel.Broadcast(ctx)
   155  		*/
   156  	case channeldpb.BroadcastType_SINGLE_CONNECTION:
   157  		var conn *Connection = nil
   158  		if msg.ClientConnId == 0 {
   159  			// server to server
   160  			conn = ctx.Channel.GetOwner().(*Connection)
   161  		} else {
   162  			// server to client
   163  			conn = GetConnection(ConnectionId(msg.ClientConnId))
   164  		}
   165  
   166  		if conn != nil && !conn.IsClosing() {
   167  			conn.Send(ctx)
   168  		} else {
   169  			ctx.Connection.Logger().Info("drop the forward message as the target connection does not exist",
   170  				zap.Uint32("msgType", uint32(ctx.MsgType)),
   171  				zap.Uint32("targetConnId", msg.ClientConnId),
   172  			)
   173  		}
   174  
   175  	default:
   176  		if ctx.Broadcast >= uint32(channeldpb.BroadcastType_ALL) && ctx.Broadcast < uint32(channeldpb.BroadcastType_ADJACENT_CHANNELS) {
   177  			ctx.Channel.Broadcast(ctx)
   178  		} else if channeldpb.BroadcastType_ADJACENT_CHANNELS.Check(ctx.Broadcast) {
   179  			if ctx.Channel.channelType != channeldpb.ChannelType_SPATIAL {
   180  				ctx.Connection.Logger().Warn("BroadcastType_ADJACENT_CHANNELS only works for Spatial channel")
   181  				return
   182  			}
   183  			if spatialController == nil {
   184  				ctx.Connection.Logger().Error("spatial controller doesn't exist")
   185  				return
   186  			}
   187  			channelIds, err := spatialController.GetAdjacentChannels(ctx.Channel.id)
   188  			if err != nil {
   189  				ctx.Connection.Logger().Error("failed to retrieve spatial regions", zap.Error(err))
   190  				return
   191  			}
   192  			// Add the connections in the owner(center) channel?
   193  			if !channeldpb.BroadcastType_ALL_BUT_OWNER.Check(ctx.Broadcast) {
   194  				channelIds = append(channelIds, ctx.Channel.id)
   195  			}
   196  
   197  			// Merge all connection in the adjacent channels to one map, to avoid duplicate send.
   198  			adjacentConns := make(map[ConnectionInChannel]struct{})
   199  			for _, id := range channelIds {
   200  				channel := GetChannel(id)
   201  				if channel == nil {
   202  					ctx.Connection.Logger().Error("invalid channel id for broadcast", zap.Uint32("channelId", uint32(id)))
   203  					continue
   204  				}
   205  				conns := channel.GetAllConnections()
   206  				for conn := range conns {
   207  					adjacentConns[conn] = struct{}{}
   208  				}
   209  			}
   210  			for conn := range adjacentConns {
   211  				// Ignore the sender?
   212  				if channeldpb.BroadcastType_ALL_BUT_SENDER.Check(ctx.Broadcast) && conn == ctx.Connection {
   213  					continue
   214  				}
   215  				// Ignore the clients?
   216  				if channeldpb.BroadcastType_ALL_BUT_CLIENT.Check(ctx.Broadcast) && conn.GetConnectionType() == channeldpb.ConnectionType_CLIENT {
   217  					continue
   218  				}
   219  				// Ignore the servers?
   220  				if channeldpb.BroadcastType_ALL_BUT_SERVER.Check(ctx.Broadcast) && conn.GetConnectionType() == channeldpb.ConnectionType_SERVER {
   221  					continue
   222  				}
   223  				// Ignore the client specified in the ServerForwardMessage
   224  				if conn.Id() == ConnectionId(msg.ClientConnId) {
   225  					continue
   226  				}
   227  				conn.Send(ctx)
   228  			}
   229  		}
   230  	}
   231  }
   232  
   233  func handleAuth(ctx MessageContext) {
   234  	if ctx.Channel != globalChannel {
   235  		ctx.Connection.Logger().Error("illegal attemp to authenticate outside the GLOBAL channel")
   236  		ctx.Connection.Close()
   237  		return
   238  	}
   239  	msg, ok := ctx.Msg.(*channeldpb.AuthMessage)
   240  	if !ok {
   241  		ctx.Connection.Logger().Error("mssage is not an AuthMessage, will not be handled.")
   242  		ctx.Connection.Close()
   243  		return
   244  	}
   245  	//log.Printf("Auth PIT: %s, LT: %s\n", msg.PlayerIdentifierToken, msg.LoginToken)
   246  
   247  	_, banned := pitBlacklist[msg.PlayerIdentifierToken]
   248  	if banned {
   249  		securityLogger.Info("refused authentication of banned PIT", zap.String("pit", msg.PlayerIdentifierToken))
   250  		ctx.Connection.Close()
   251  		return
   252  	}
   253  
   254  	if authProvider == nil && !GlobalSettings.Development {
   255  		rootLogger.Panic("no auth provider")
   256  		return
   257  	}
   258  
   259  	authResult := channeldpb.AuthResultMessage_SUCCESSFUL
   260  	if ctx.Connection.GetConnectionType() == channeldpb.ConnectionType_SERVER && GlobalSettings.ServerBypassAuth {
   261  		onAuthComplete(ctx, authResult, msg.PlayerIdentifierToken)
   262  	} else if authProvider != nil {
   263  		go func() {
   264  			authResult, err := authProvider.DoAuth(ctx.Connection.Id(), msg.PlayerIdentifierToken, msg.LoginToken)
   265  			if err != nil {
   266  				ctx.Connection.Logger().Error("failed to do auth", zap.Error(err))
   267  				ctx.Connection.Close()
   268  			} else {
   269  				onAuthComplete(ctx, authResult, msg.PlayerIdentifierToken)
   270  			}
   271  		}()
   272  	} else {
   273  		onAuthComplete(ctx, authResult, msg.PlayerIdentifierToken)
   274  	}
   275  }
   276  
   277  func onAuthComplete(ctx MessageContext, authResult channeldpb.AuthResultMessage_AuthResult, pit string) {
   278  	if ctx.Connection.IsClosing() {
   279  		return
   280  	}
   281  
   282  	if authResult == channeldpb.AuthResultMessage_SUCCESSFUL {
   283  		ctx.Connection.OnAuthenticated(pit)
   284  	}
   285  
   286  	ctx.Msg = &channeldpb.AuthResultMessage{
   287  		Result:          authResult,
   288  		ConnId:          uint32(ctx.Connection.Id()),
   289  		CompressionType: GlobalSettings.CompressionType,
   290  	}
   291  	ctx.Connection.Send(ctx)
   292  
   293  	// Also send the respond to The GLOBAL channel owner (to handle the client's subscription if it doesn't have the authority to).
   294  	if globalChannel.HasOwner() {
   295  		ctx.StubId = 0
   296  		globalChannel.SendToOwner(ctx)
   297  	}
   298  
   299  	Event_AuthComplete.Broadcast(AuthEventData{
   300  		AuthResult:            authResult,
   301  		Connection:            ctx.Connection,
   302  		PlayerIdentifierToken: pit,
   303  	})
   304  }
   305  
   306  func handleCreateChannel(ctx MessageContext) {
   307  	// Only the GLOBAL channel can handle channel creation/deletion/listing
   308  	if ctx.Channel != globalChannel {
   309  		ctx.Connection.Logger().Error("illegal attemp to create channel outside the GLOBAL channel")
   310  		return
   311  	}
   312  
   313  	msg, ok := ctx.Msg.(*channeldpb.CreateChannelMessage)
   314  	if !ok {
   315  		ctx.Connection.Logger().Error("message is not a CreateChannelMessage, will not be handled.")
   316  		return
   317  	}
   318  
   319  	var newChannel *Channel
   320  	var err error
   321  	if msg.ChannelType == channeldpb.ChannelType_UNKNOWN {
   322  		ctx.Connection.Logger().Error("illegal attemp to create the UNKNOWN channel")
   323  		return
   324  	} else if msg.ChannelType == channeldpb.ChannelType_GLOBAL {
   325  		// Global channel is initially created by the system. Creating the channel will attempt to own it.
   326  		newChannel = globalChannel
   327  		if !globalChannel.HasOwner() {
   328  			globalChannel.SetOwner(ctx.Connection)
   329  			Event_GlobalChannelPossessed.Broadcast(globalChannel)
   330  			ctx.Connection.Logger().Info("owned the GLOBAL channel")
   331  		} else {
   332  			ctx.Connection.Logger().Error("illegal attemp to create the GLOBAL channel")
   333  			return
   334  		}
   335  	} else if msg.ChannelType == channeldpb.ChannelType_SPATIAL {
   336  		handleCreateSpatialChannel(ctx, msg)
   337  		return
   338  	} else {
   339  		newChannel, err = CreateChannel(msg.ChannelType, ctx.Connection)
   340  		if err != nil {
   341  			ctx.Connection.Logger().Error("failed to create channel",
   342  				zap.Uint32("channelType", uint32(msg.ChannelType)),
   343  				zap.Error(err),
   344  			)
   345  			return
   346  		}
   347  		newChannel.Logger().Info("created channel with owner", zap.Uint32("ownerConnId", uint32(newChannel.GetOwner().Id())))
   348  	}
   349  
   350  	newChannel.metadata = msg.Metadata
   351  	if msg.Data != nil {
   352  		dataMsg, err := msg.Data.UnmarshalNew()
   353  		if err != nil {
   354  			newChannel.Logger().Error("failed to unmarshal data message for the new channel", zap.Error(err))
   355  			return
   356  		} else {
   357  			newChannel.InitData(dataMsg, msg.MergeOptions)
   358  		}
   359  	} else {
   360  		// Channel data should always be initialized
   361  		newChannel.InitData(nil, msg.MergeOptions)
   362  	}
   363  
   364  	ctx.Msg = &channeldpb.CreateChannelResultMessage{
   365  		ChannelType: newChannel.channelType,
   366  		Metadata:    newChannel.metadata,
   367  		OwnerConnId: uint32(ctx.Connection.Id()),
   368  		ChannelId:   uint32(newChannel.id),
   369  	}
   370  	ctx.Connection.Send(ctx)
   371  	// Also send the response to the GLOBAL channel owner.
   372  	if globalChannel.GetOwner() != ctx.Connection && globalChannel.HasOwner() {
   373  		ctx.StubId = 0
   374  		globalChannel.SendToOwner(ctx)
   375  	}
   376  
   377  	// Subscribe to channel after creation
   378  	cs, _ := ctx.Connection.SubscribeToChannel(newChannel, msg.SubOptions)
   379  	if cs != nil {
   380  		ctx.Connection.sendSubscribed(ctx, newChannel, ctx.Connection, 0, &cs.options)
   381  	}
   382  }
   383  
   384  func handleRemoveChannel(ctx MessageContext) {
   385  
   386  	msg, ok := ctx.Msg.(*channeldpb.RemoveChannelMessage)
   387  	if !ok {
   388  		ctx.Connection.Logger().Error("message is not a RemoveChannelMessage, will not be handled.")
   389  		return
   390  	}
   391  
   392  	channelToRemove := GetChannel(common.ChannelId(msg.ChannelId))
   393  	if channelToRemove == nil {
   394  		ctx.Connection.Logger().Error("invalid channelId for removing", zap.Uint32("channelId", msg.ChannelId))
   395  		return
   396  	}
   397  
   398  	// Check ACL from settings
   399  	// If ctx.Connection == nil, the removal is triggered internally (e.g. ChannelSettings.RemoveChannelAfterOwnerRemoved)
   400  	hasAccess, err := channelToRemove.CheckACL(ctx.Connection, ChannelAccessType_Remove)
   401  	if ctx.HasConnection() && !hasAccess {
   402  		ownerConnId := uint32(0)
   403  		if channelToRemove.HasOwner() {
   404  			ownerConnId = uint32(channelToRemove.GetOwner().Id())
   405  		}
   406  		ctx.Connection.Logger().Error("connection doesn't have access to remove channel",
   407  			zap.String("channelType", channelToRemove.channelType.String()),
   408  			zap.Uint32("channelId", uint32(channelToRemove.id)),
   409  			zap.Uint32("ownerConnId", ownerConnId),
   410  			zap.Error(err))
   411  		return
   412  	}
   413  
   414  	for sc := range channelToRemove.subscribedConnections {
   415  		if sc != nil {
   416  			//sc.sendUnsubscribed(ctx, channelToRemove, 0)
   417  			response := ctx
   418  			response.StubId = 0
   419  			sc.Send(response)
   420  		}
   421  	}
   422  	RemoveChannel(channelToRemove)
   423  
   424  	var logger *Logger
   425  	if ctx.HasConnection() {
   426  		logger = ctx.Connection.Logger()
   427  	} else {
   428  		logger = RootLogger()
   429  	}
   430  	logger.Info("removed channel",
   431  		zap.String("channelType", channelToRemove.channelType.String()),
   432  		zap.Uint32("channelId", uint32(channelToRemove.id)),
   433  		zap.Int("subs", len(channelToRemove.subscribedConnections)),
   434  	)
   435  }
   436  
   437  func handleListChannel(ctx MessageContext) {
   438  	if ctx.Channel != globalChannel {
   439  		ctx.Connection.Logger().Error("illegal attemp to list channel outside the GLOBAL channel")
   440  		return
   441  	}
   442  
   443  	msg, ok := ctx.Msg.(*channeldpb.ListChannelMessage)
   444  	if !ok {
   445  		ctx.Connection.Logger().Error("message is not a ListChannelMessage, will not be handled.")
   446  		return
   447  	}
   448  
   449  	result := make([]*channeldpb.ListChannelResultMessage_ChannelInfo, 0)
   450  	allChannels.Range(func(_ common.ChannelId, channel *Channel) bool {
   451  		if msg.TypeFilter != channeldpb.ChannelType_UNKNOWN && msg.TypeFilter != channel.channelType {
   452  			return true
   453  		}
   454  		matched := len(msg.MetadataFilters) == 0
   455  		for _, keyword := range msg.MetadataFilters {
   456  			if strings.Contains(channel.metadata, keyword) {
   457  				matched = true
   458  				break
   459  			}
   460  		}
   461  		if matched {
   462  			result = append(result, &channeldpb.ListChannelResultMessage_ChannelInfo{
   463  				ChannelId:   uint32(channel.id),
   464  				ChannelType: channel.channelType,
   465  				Metadata:    channel.metadata,
   466  			})
   467  		}
   468  		return true
   469  	})
   470  
   471  	ctx.Msg = &channeldpb.ListChannelResultMessage{
   472  		Channels: result,
   473  	}
   474  	ctx.Connection.Send(ctx)
   475  }
   476  
   477  func handleSubToChannel(ctx MessageContext) {
   478  	msg, ok := ctx.Msg.(*channeldpb.SubscribedToChannelMessage)
   479  	if !ok {
   480  		ctx.Connection.Logger().Error("message is not a SubscribedToChannelMessage, will not be handled.")
   481  		return
   482  	}
   483  
   484  	var connToSub *Connection
   485  	if ctx.Connection.GetConnectionType() == channeldpb.ConnectionType_CLIENT {
   486  		connToSub = ctx.Connection.(*Connection)
   487  	} else {
   488  		// Only the server can specify a ConnId.
   489  		connToSub = GetConnection(ConnectionId(msg.ConnId))
   490  	}
   491  
   492  	if connToSub == nil {
   493  		ctx.Connection.Logger().Error("invalid ConnectionId for sub", zap.Uint32("connIdInMsg", msg.ConnId))
   494  		return
   495  	}
   496  
   497  	hasAccess, err := ctx.Channel.CheckACL(ctx.Connection, ChannelAccessType_Sub)
   498  	if connToSub.Id() != ctx.Connection.Id() && !hasAccess {
   499  		ctx.Connection.Logger().Warn("connection doesn't have access to sub connection to this channel",
   500  			zap.Uint32("subConnId", msg.ConnId),
   501  			zap.String("channelType", ctx.Channel.channelType.String()),
   502  			zap.Uint32("channelId", uint32(ctx.Channel.id)),
   503  			zap.Error(err),
   504  		)
   505  		return
   506  	}
   507  
   508  	/*
   509  		cs, exists := ctx.Channel.subscribedConnections[connToSub]
   510  		if exists {
   511  			ctx.Connection.Logger().Debug("already subscribed to channel, the subscription options will be merged",
   512  				zap.String("channelType", ctx.Channel.channelType.String()),
   513  				zap.Uint32("channelId", uint32(ctx.Channel.id)),
   514  			)
   515  			if msg.SubOptions != nil {
   516  				proto.Merge(&cs.options, msg.SubOptions)
   517  			}
   518  			connToSub.sendSubscribed(ctx, ctx.Channel, connToSub, 0, &cs.options)
   519  			// Do not send the SubscribedToChannelResultMessage to the sender or channel owner if already subed.
   520  			return
   521  		}
   522  	*/
   523  
   524  	cs, shouldSend := connToSub.SubscribeToChannel(ctx.Channel, msg.SubOptions)
   525  	if !shouldSend {
   526  		return
   527  	}
   528  
   529  	// Always notify the sender - may need to update the sub options.
   530  	ctx.Connection.sendSubscribed(ctx, ctx.Channel, connToSub, ctx.StubId, &cs.options)
   531  
   532  	// Notify the subscribed if it's not the sender.
   533  	if connToSub != ctx.Connection {
   534  		connToSub.sendSubscribed(ctx, ctx.Channel, connToSub, 0, &cs.options)
   535  	}
   536  
   537  	// Notify the channel owner if not already subed and it's not the sender.
   538  	if ownerConn := ctx.Channel.GetOwner(); ownerConn != nil && ownerConn != ctx.Connection && !ownerConn.IsClosing() {
   539  		ownerConn.sendSubscribed(ctx, ctx.Channel, connToSub, 0, &cs.options)
   540  	}
   541  }
   542  
   543  func handleUnsubFromChannel(ctx MessageContext) {
   544  	msg, ok := ctx.Msg.(*channeldpb.UnsubscribedFromChannelMessage)
   545  	if !ok {
   546  		ctx.Connection.Logger().Error("message is not a UnsubscribedFromChannelMessage, will not be handled.")
   547  		return
   548  	}
   549  
   550  	// The connection that unsubscribes. Could be different to the connection that sends the message.
   551  	connToUnsub := GetConnection(ConnectionId(msg.ConnId))
   552  	if connToUnsub == nil {
   553  		ctx.Connection.Logger().Error("invalid ConnectionId for unsub", zap.Uint32("connId", msg.ConnId))
   554  		return
   555  	}
   556  
   557  	hasAccess, accessErr := ctx.Channel.CheckACL(ctx.Connection, ChannelAccessType_Unsub)
   558  	if connToUnsub.id != ctx.Connection.Id() && !hasAccess {
   559  		ctx.Connection.Logger().Error("connection dosen't have access to unsub connection from this channel",
   560  			zap.Uint32("unsubConnId", msg.ConnId),
   561  			zap.String("channelType", ctx.Channel.channelType.String()),
   562  			zap.Uint32("channelId", uint32(ctx.Channel.id)),
   563  			zap.Error(accessErr),
   564  		)
   565  		return
   566  	}
   567  
   568  	_, err := connToUnsub.UnsubscribeFromChannel(ctx.Channel)
   569  	if err != nil {
   570  		ctx.Connection.Logger().Warn("failed to unsub from channel",
   571  			zap.String("channelType", ctx.Channel.channelType.String()),
   572  			zap.Uint32("channelId", uint32(ctx.Channel.id)),
   573  			zap.Error(err),
   574  		)
   575  		return
   576  	}
   577  
   578  	// Notify the sender.
   579  	ctx.Connection.sendUnsubscribed(ctx, ctx.Channel, connToUnsub, ctx.StubId)
   580  
   581  	// Notify the unsubscribed.
   582  	if connToUnsub != ctx.Connection {
   583  		connToUnsub.sendUnsubscribed(ctx, ctx.Channel, connToUnsub, 0)
   584  	}
   585  	// Notify the channel owner.
   586  	if ownerConn := ctx.Channel.GetOwner(); ownerConn != nil && !ownerConn.IsClosing() {
   587  		if ownerConn != ctx.Connection && ownerConn != connToUnsub {
   588  			ownerConn.sendUnsubscribed(ctx, ctx.Channel, connToUnsub, 0)
   589  		} else if ownerConn == connToUnsub {
   590  			// Reset the owner if it unsubscribed itself
   591  			ctx.Channel.SetOwner(nil)
   592  		}
   593  	}
   594  }
   595  
   596  func handleChannelDataUpdate(ctx MessageContext) {
   597  	// Only channel owner or writable subsciptors can update the data
   598  	if ownerConn := ctx.Channel.GetOwner(); ownerConn != ctx.Connection {
   599  		cs := ctx.Channel.subscribedConnections[ctx.Connection]
   600  		if cs == nil || *cs.options.DataAccess != channeldpb.ChannelDataAccess_WRITE_ACCESS {
   601  			if ctx.Connection.GetConnectionType() == channeldpb.ConnectionType_SERVER && ownerConn != nil && !ownerConn.IsClosing() {
   602  				// Quick fix: set the sender to the channel owner if it's a server connection
   603  				ctx.Connection = ownerConn
   604  			} else {
   605  				ctx.Connection.Logger().Warn("attempt to update channel data but has no access",
   606  					zap.String("channelType", ctx.Channel.channelType.String()),
   607  					zap.Uint32("channelId", uint32(ctx.Channel.id)),
   608  				)
   609  				return
   610  			}
   611  		}
   612  	}
   613  
   614  	if ctx.Channel.Data() == nil {
   615  		ctx.Channel.Logger().Info("channel data is not initialized - should send CreateChannelMessage before ChannelDataUpdateMessage",
   616  			zap.Uint32("connId", uint32(ctx.Connection.Id())))
   617  		return
   618  	}
   619  
   620  	msg, ok := ctx.Msg.(*channeldpb.ChannelDataUpdateMessage)
   621  	if !ok {
   622  		ctx.Connection.Logger().Error("message is not a ChannelDataUpdateMessage, will not be handled.")
   623  		return
   624  	}
   625  	updateMsg, err := msg.Data.UnmarshalNew()
   626  	if err != nil {
   627  		ctx.Connection.Logger().Error("failed to unmarshal channel update data", zap.Error(err),
   628  			zap.String("channelType", ctx.Channel.channelType.String()),
   629  			zap.String("typeUrl", msg.Data.TypeUrl))
   630  		return
   631  	}
   632  
   633  	if ctx.Channel.spatialNotifier != nil {
   634  		if ctx.Connection.GetConnectionType() == channeldpb.ConnectionType_CLIENT {
   635  			ctx.Channel.SetDataUpdateConnId(ctx.Connection.Id())
   636  		} else {
   637  			ctx.Channel.SetDataUpdateConnId(ConnectionId(msg.ContextConnId))
   638  		}
   639  	}
   640  	ctx.Channel.Data().OnUpdate(updateMsg, ctx.arrivalTime, ctx.Connection.Id(), ctx.Channel.spatialNotifier)
   641  }
   642  
   643  func handleDisconnect(ctx MessageContext) {
   644  	if ctx.Channel != globalChannel {
   645  		ctx.Connection.Logger().Error("illegal attemp to disconnect another connection outside the GLOBAL channel")
   646  		return
   647  	}
   648  
   649  	msg, ok := ctx.Msg.(*channeldpb.DisconnectMessage)
   650  	if !ok {
   651  		ctx.Connection.Logger().Error("message is not a DisconnectMessage, will not be handled.")
   652  		return
   653  	}
   654  
   655  	connToDisconnect := GetConnection(ConnectionId(msg.ConnId))
   656  	if connToDisconnect == nil {
   657  		ctx.Connection.Logger().Warn("could not find the connection to disconnect",
   658  			zap.Uint32("targetConnId", msg.ConnId),
   659  		)
   660  		return
   661  	}
   662  
   663  	if err := connToDisconnect.Disconnect(); err != nil {
   664  		ctx.Connection.Logger().Warn("failed to disconnect a connection",
   665  			zap.Uint32("targetConnId", msg.ConnId),
   666  			zap.String("targetConnType", connToDisconnect.connectionType.String()),
   667  		)
   668  	} else {
   669  		ctx.Connection.Logger().Info("successfully disconnected a connection",
   670  			zap.Uint32("targetConnId", msg.ConnId),
   671  			zap.String("targetConnType", connToDisconnect.connectionType.String()),
   672  		)
   673  	}
   674  	connToDisconnect.Close()
   675  }