2023-07-23 21:31:13 +00:00
using SharpChat.Events ;
using SharpChat.EventStorage ;
2024-05-20 23:40:34 +00:00
using SharpChat.SockChat ;
using SharpChat.SockChat.PacketsS2C ;
2022-08-30 15:00:58 +00:00
using System ;
using System.Collections.Generic ;
2023-02-10 06:07:59 +00:00
using System.Linq ;
2023-02-19 22:27:08 +00:00
using System.Threading ;
2022-08-30 15:00:58 +00:00
2024-05-09 21:31:19 +00:00
namespace SharpChat {
2024-05-24 03:44:20 +00:00
public class SockChatContext : IChatEventHandler {
2023-02-19 22:27:08 +00:00
public readonly SemaphoreSlim ContextAccess = new ( 1 , 1 ) ;
2022-08-30 15:00:58 +00:00
2024-05-24 03:44:20 +00:00
public IEventStorage EventStorage { get ; }
public ChatEventDispatcher Events { get ; } = new ( ) ;
2024-05-24 19:17:12 +00:00
public ConnectionsContext Connections { get ; } = new ( ) ;
public ChannelsContext Channels { get ; } = new ( ) ;
public UsersContext Users { get ; } = new ( ) ;
public UserStatusContext UserStatuses { get ; } = new ( ) ;
2024-05-19 21:02:17 +00:00
public ChannelsUsersContext ChannelsUsers { get ; } = new ( ) ;
2023-02-19 22:27:08 +00:00
public Dictionary < long , RateLimiter > UserRateLimiters { get ; } = new ( ) ;
2023-02-17 19:02:35 +00:00
2024-05-20 23:40:34 +00:00
public SockChatContext ( IEventStorage evtStore ) {
2024-05-24 03:44:20 +00:00
EventStorage = evtStore ;
Events . Subscribe ( evtStore ) ;
Events . Subscribe ( this ) ;
2022-08-30 15:00:58 +00:00
}
2024-05-24 03:44:20 +00:00
public void HandleEvent ( ChatEventInfo info ) {
2024-05-24 19:17:12 +00:00
UserStatusInfo userStatusInfo = UserStatuses . Get ( info . SenderId ) ;
2024-05-24 03:44:20 +00:00
2024-05-24 16:01:11 +00:00
if ( ! string . IsNullOrWhiteSpace ( info . ChannelName ) )
ChannelsUsers . SetUserLastChannel ( info . SenderId , info . ChannelName ) ;
// TODO: should user:connect and user:disconnect be channel agnostic?
2024-05-24 03:44:20 +00:00
switch ( info . Type ) {
2024-05-29 20:51:37 +00:00
case "user:add" :
Users . Add ( new UserInfo (
info . SenderId ,
info . SenderName ,
info . SenderColour ,
info . SenderRank ,
info . SenderPerms ,
info . SenderNickName ,
info . Data is UserAddEventData uaData & & uaData . IsSuper
) ) ;
break ;
case "user:delete" :
UserStatuses . Clear ( info . SenderId ) ;
Users . Remove ( info . SenderId ) ;
break ;
2024-05-24 03:44:20 +00:00
case "user:connect" :
2024-05-29 20:51:37 +00:00
if ( info . Data is not UserConnectEventData ucData | | ! ucData . Notify )
break ;
2024-05-24 03:44:20 +00:00
SendTo ( info . ChannelName , new UserConnectS2CPacket (
info . Id ,
info . Created ,
info . SenderId ,
2024-05-24 19:17:12 +00:00
SockChatUtility . GetUserName ( info , userStatusInfo ) ,
2024-05-24 03:44:20 +00:00
info . SenderColour ,
info . SenderRank ,
info . SenderPerms
) ) ;
2024-05-29 20:51:37 +00:00
ChannelsUsers . Join ( info . ChannelName , info . SenderId ) ;
2024-05-24 03:44:20 +00:00
break ;
case "user:disconnect" :
2024-05-24 16:01:11 +00:00
ChannelInfo [ ] channels = Channels . GetMany ( ChannelsUsers . GetUserChannelNames ( info . SenderId ) ) ;
ChannelsUsers . DeleteUser ( info . SenderId ) ;
if ( channels . Length > 0 ) {
UserDisconnectS2CPacket udPacket = new (
info . Id ,
info . Created ,
info . SenderId ,
2024-05-24 19:17:12 +00:00
SockChatUtility . GetUserName ( info , userStatusInfo ) ,
2024-05-24 16:01:11 +00:00
info . Data is UserDisconnectEventData userDisconnect ? userDisconnect . Reason : UserDisconnectReason . Leave
) ;
foreach ( ChannelInfo chan in channels ) {
2024-05-29 20:51:37 +00:00
if ( chan . IsTemporary & & chan . OwnerId = = info . SenderId )
Events . Dispatch ( "chan:delete" , chan . Name , info ) ;
2024-05-24 16:01:11 +00:00
else
SendTo ( chan , udPacket ) ;
}
}
break ;
2024-05-24 19:17:12 +00:00
case "user:status" :
if ( info . Data is not UserStatusUpdateEventData userStatusUpdate )
break ;
if ( userStatusInfo . Status = = userStatusUpdate . Status
& & userStatusInfo . Text . Equals ( userStatusUpdate . Text ) )
break ;
userStatusInfo = UserStatuses . Set (
info . SenderId ,
userStatusUpdate . Status ,
userStatusUpdate . Text ? ? string . Empty
) ;
SendToUserChannels ( info . SenderId , new UserUpdateS2CPacket (
info . SenderId ,
SockChatUtility . GetUserName ( info , userStatusInfo ) ,
info . SenderColour ,
info . SenderRank ,
info . SenderPerms
) ) ;
break ;
case "user:update" :
2024-05-29 20:51:37 +00:00
if ( info . Data is not UserUpdateEventData userUpdate )
break ;
UserInfo ? uuUserInfo = Users . Get ( info . SenderId ) ;
if ( uuUserInfo is null )
2024-05-24 19:17:12 +00:00
break ;
bool uuHasChanged = false ;
string? uuPrevName = null ;
2024-05-29 20:51:37 +00:00
if ( userUpdate . Name ! = null & & ! userUpdate . Name . Equals ( uuUserInfo . UserName ) ) {
uuUserInfo . UserName = userUpdate . Name ;
2024-05-24 19:17:12 +00:00
uuHasChanged = true ;
}
2024-05-29 20:51:37 +00:00
if ( userUpdate . NickName ! = null & & ! userUpdate . NickName . Equals ( uuUserInfo . NickName ) ) {
2024-05-24 19:17:12 +00:00
if ( userUpdate . Notify )
2024-05-29 20:51:37 +00:00
uuPrevName = string . IsNullOrWhiteSpace ( uuUserInfo . NickName ) ? uuUserInfo . UserName : uuUserInfo . NickName ;
2024-05-24 19:17:12 +00:00
2024-05-29 20:51:37 +00:00
uuUserInfo . NickName = userUpdate . NickName ;
2024-05-24 19:17:12 +00:00
uuHasChanged = true ;
}
2024-05-29 20:51:37 +00:00
if ( userUpdate . Colour . HasValue & & userUpdate . Colour ! = uuUserInfo . Colour . ToMisuzu ( ) ) {
uuUserInfo . Colour = Colour . FromMisuzu ( userUpdate . Colour . Value ) ;
2024-05-24 19:17:12 +00:00
uuHasChanged = true ;
}
2024-05-29 20:51:37 +00:00
if ( userUpdate . Rank ! = null & & userUpdate . Rank ! = uuUserInfo . Rank ) {
uuUserInfo . Rank = userUpdate . Rank . Value ;
2024-05-24 19:17:12 +00:00
uuHasChanged = true ;
}
2024-05-29 20:51:37 +00:00
if ( userUpdate . Perms . HasValue & & userUpdate . Perms ! = uuUserInfo . Permissions ) {
uuUserInfo . Permissions = userUpdate . Perms . Value ;
2024-05-24 19:17:12 +00:00
uuHasChanged = true ;
}
2024-05-29 20:51:37 +00:00
if ( userUpdate . IsSuper . HasValue & & userUpdate . IsSuper ! = uuUserInfo . IsSuper )
uuUserInfo . IsSuper = userUpdate . IsSuper . Value ;
2024-05-24 19:17:12 +00:00
if ( uuHasChanged ) {
if ( uuPrevName ! = null )
SendToUserChannels ( info . SenderId , new UserNickChangeS2CPacket (
info . Id ,
info . Created ,
string . IsNullOrWhiteSpace ( info . SenderNickName ) ? uuPrevName : $"~{info.SenderNickName}" ,
2024-05-29 20:51:37 +00:00
SockChatUtility . GetUserName ( uuUserInfo , userStatusInfo )
2024-05-24 19:17:12 +00:00
) ) ;
SendToUserChannels ( info . SenderId , new UserUpdateS2CPacket (
2024-05-29 20:51:37 +00:00
uuUserInfo . UserId ,
SockChatUtility . GetUserName ( uuUserInfo , userStatusInfo ) ,
uuUserInfo . Colour ,
uuUserInfo . Rank ,
uuUserInfo . Permissions
2024-05-24 19:17:12 +00:00
) ) ;
}
break ;
2024-05-24 16:01:11 +00:00
case "user:kickban" :
if ( info . Data is not UserKickBanEventData userBaka )
break ;
SendTo ( info . SenderId , new ForceDisconnectS2CPacket ( userBaka . Expires ) ) ;
ConnectionInfo [ ] conns = Connections . GetUser ( info . SenderId ) ;
foreach ( ConnectionInfo conn in conns ) {
Connections . Remove ( conn ) ;
if ( conn is SockChatConnectionInfo scConn )
scConn . Close ( 1000 ) ;
Logger . Write ( $"<{conn.RemoteEndPoint}> Nuked connection from banned user #{conn.UserId}." ) ;
}
string bakaChannelName = ChannelsUsers . GetUserLastChannel ( info . SenderId ) ;
if ( ! string . IsNullOrWhiteSpace ( bakaChannelName ) )
Events . Dispatch ( new ChatEventInfo (
SharpId . Next ( ) ,
"user:disconnect" ,
info . Created ,
bakaChannelName ,
info . SenderId ,
info . SenderName ,
info . SenderColour ,
info . SenderRank ,
info . SenderNickName ,
info . SenderPerms ,
new UserDisconnectEventData ( userBaka . Reason )
) ) ;
2024-05-24 03:44:20 +00:00
break ;
case "chan:join" :
2024-05-29 20:51:37 +00:00
HandleUserChannelJoin (
info . ChannelName ,
new UserInfo ( info ) , // kinda stinky
userStatusInfo
) ;
2024-05-24 03:44:20 +00:00
break ;
case "chan:leave" :
2024-05-29 20:51:37 +00:00
ChannelsUsers . Leave ( info . ChannelName , info . SenderId ) ;
2024-05-24 03:44:20 +00:00
SendTo ( info . ChannelName , new UserChannelLeaveS2CPacket ( info . SenderId ) ) ;
2024-05-29 20:51:37 +00:00
ChannelInfo ? clChannelInfo = Channels . Get ( info . ChannelName ) ;
if ( clChannelInfo ? . IsTemporary = = true & & clChannelInfo . OwnerId = = info . SenderId )
Events . Dispatch ( "chan:delete" , clChannelInfo . Name , info ) ;
break ;
case "chan:add" :
if ( info . Data is not ChannelAddEventData caData )
break ;
ChannelInfo caChannelInfo = new (
info . ChannelName ,
caData . Password ,
caData . IsTemporary ,
caData . MinRank ,
info . SenderId
) ;
Channels . Add ( caChannelInfo ) ;
foreach ( UserInfo ccu in Users . GetMany ( minRank : caChannelInfo . Rank ) )
SendTo ( ccu , new ChannelCreateS2CPacket (
caChannelInfo . Name ,
caChannelInfo . HasPassword ,
caChannelInfo . IsTemporary
) ) ;
break ;
case "chan:update" :
if ( info . Data is not ChannelUpdateEventData cuData )
break ;
ChannelInfo ? cuChannelInfo = Channels . Get ( info . ChannelName ) ;
if ( cuChannelInfo is null )
break ;
string cuChannelName = cuChannelInfo . Name ;
if ( ! string . IsNullOrEmpty ( cuData . Name ) )
cuChannelInfo . Name = cuData . Name ;
if ( cuData . MinRank . HasValue )
cuChannelInfo . Rank = cuData . MinRank . Value ;
if ( cuData . Password ! = null ) // this should probably be hashed
cuChannelInfo . Password = cuData . Password ;
if ( cuData . IsTemporary . HasValue )
cuChannelInfo . IsTemporary = cuData . IsTemporary . Value ;
bool nameChanged = ! cuChannelName . Equals ( cuChannelInfo . Name , StringComparison . InvariantCultureIgnoreCase ) ;
if ( nameChanged )
ChannelsUsers . RenameChannel ( cuChannelName , cuChannelInfo . Name ) ;
// TODO: Users that no longer have access to the channel/gained access to the channel by the rank change should receive delete and create packets respectively
// the server currently doesn't keep track of what channels a user is already aware of so can't really simulate this yet.
foreach ( UserInfo user in Users . GetMany ( minRank : cuChannelInfo . Rank ) )
SendTo ( user , new ChannelUpdateS2CPacket (
cuChannelName ,
cuChannelInfo . Name ,
cuChannelInfo . HasPassword ,
cuChannelInfo . IsTemporary
) ) ;
if ( nameChanged )
SendTo ( cuChannelInfo , new UserChannelForceJoinS2CPacket ( cuChannelInfo . Name ) ) ;
break ;
case "chan:delete" :
ChannelInfo ? cdTargetChannelInfo = Channels . Get ( info . ChannelName ) ;
ChannelInfo ? cdMainChannelInfo = Channels . MainChannel ;
if ( cdTargetChannelInfo = = null | | cdMainChannelInfo = = null | | cdTargetChannelInfo = = Channels . MainChannel )
break ;
// Remove channel from the listing
Channels . Remove ( info . ChannelName ) ;
// Move all users back to the main channel
UserInfo [ ] cdUserInfos = Users . GetMany ( ChannelsUsers . GetChannelUserIds ( info . ChannelName ) ) ;
ChannelsUsers . DeleteChannel ( cdMainChannelInfo ) ;
foreach ( UserInfo userInfo in cdUserInfos )
HandleUserChannelJoin (
info . ChannelName ,
userInfo ,
UserStatuses . Get ( userInfo )
) ;
// Broadcast deletion of channel
foreach ( UserInfo user in Users . GetMany ( minRank : cdTargetChannelInfo . Rank ) )
SendTo ( user , new ChannelDeleteS2CPacket ( cdTargetChannelInfo . Name ) ) ;
2024-05-24 03:44:20 +00:00
break ;
case "msg:delete" :
if ( info . Data is not MessageDeleteEventData msgDelete )
break ;
2023-07-23 21:31:13 +00:00
2024-05-24 03:44:20 +00:00
MessageDeleteS2CPacket msgDelPacket = new ( msgDelete . MessageId ) ;
if ( info . IsBroadcast ) {
Send ( msgDelPacket ) ;
} else if ( info . ChannelName . StartsWith ( '@' ) ) {
long [ ] targetIds = info . ChannelName [ 1. . ] . Split ( '-' , 3 ) . Select ( u = > long . TryParse ( u , out long up ) ? up : - 1 ) . ToArray ( ) ;
if ( targetIds . Length ! = 2 )
return ;
UserInfo [ ] users = Users . GetMany ( targetIds ) ;
UserInfo ? target = users . FirstOrDefault ( u = > u . UserId ! = info . SenderId ) ;
if ( target = = null )
return ;
foreach ( UserInfo user in users )
SendTo ( user , msgDelPacket ) ;
} else {
SendTo ( info . ChannelName , msgDelPacket ) ;
}
break ;
case "msg:add" :
if ( info . Data is not MessageAddEventData msgAdd )
break ;
if ( info . IsBroadcast ) {
Send ( new MessageBroadcastS2CPacket ( info . Id , info . Created , msgAdd . Text ) ) ;
} else if ( info . ChannelName . StartsWith ( '@' ) ) {
// The channel name returned by GetDMChannelName should not be exposed to the user, instead @<Target User> should be displayed
// e.g. nook sees @Arysil and Arysil sees @nook
long [ ] targetIds = info . ChannelName [ 1. . ] . Split ( '-' , 3 ) . Select ( u = > long . TryParse ( u , out long up ) ? up : - 1 ) . ToArray ( ) ;
if ( targetIds . Length ! = 2 )
return ;
UserInfo [ ] users = Users . GetMany ( targetIds ) ;
UserInfo ? target = users . FirstOrDefault ( u = > u . UserId ! = info . SenderId ) ;
if ( target = = null )
return ;
foreach ( UserInfo user in users )
SendTo ( user , new MessageAddS2CPacket (
info . Id ,
DateTimeOffset . Now ,
info . SenderId ,
info . SenderId = = user . UserId ? $"{SockChatUtility.GetUserName(target)} {msgAdd.Text}" : msgAdd . Text ,
msgAdd . IsAction ,
true
) ) ;
} else {
ChannelInfo ? channel = Channels . Get ( info . ChannelName , SockChatUtility . SanitiseChannelName ) ;
if ( channel ! = null )
SendTo ( channel , new MessageAddS2CPacket (
info . Id ,
DateTimeOffset . Now ,
info . SenderId ,
msgAdd . Text ,
msgAdd . IsAction ,
false
) ) ;
}
break ;
2023-07-23 21:31:13 +00:00
}
}
2024-05-29 20:51:37 +00:00
private void HandleUserChannelJoin ( string channelName , UserInfo userInfo , UserStatusInfo statusInfo ) {
SendTo ( userInfo . UserId , new ClearMessagesAndUsersS2CPacket ( ) ) ;
UserInfo [ ] userInfos = Users . GetMany ( ChannelsUsers . GetChannelUserIds ( channelName ) ) ;
List < UsersPopulateS2CPacket . ListEntry > userEntries = new ( ) ;
foreach ( UserInfo memberInfo in userInfos )
if ( memberInfo . UserId ! = userInfo . UserId )
userEntries . Add ( new (
memberInfo . UserId ,
SockChatUtility . GetUserName ( memberInfo , UserStatuses . Get ( memberInfo ) ) ,
memberInfo . Colour ,
memberInfo . Rank ,
memberInfo . Permissions ,
true
) ) ;
SendTo ( userInfo . UserId , new UsersPopulateS2CPacket ( userEntries . ToArray ( ) ) ) ;
SendTo ( channelName , new UserChannelJoinS2CPacket (
userInfo . UserId ,
SockChatUtility . GetUserName ( userInfo , statusInfo ) ,
userInfo . Colour ,
userInfo . Rank ,
userInfo . Permissions
) ) ;
HandleChannelEventLog ( channelName , p = > SendTo ( userInfo . UserId , p ) ) ;
ChannelsUsers . Join ( channelName , userInfo . UserId ) ;
SendTo ( userInfo . UserId , new UserChannelForceJoinS2CPacket ( channelName ) ) ;
}
2022-08-30 15:00:58 +00:00
public void Update ( ) {
2024-05-21 20:08:23 +00:00
ConnectionInfo [ ] timedOut = Connections . GetTimedOut ( ) ;
foreach ( ConnectionInfo conn in timedOut ) {
2024-05-20 16:16:32 +00:00
Connections . Remove ( conn ) ;
2024-05-21 20:08:23 +00:00
if ( conn is SockChatConnectionInfo scConn )
scConn . Close ( 1002 ) ;
2023-02-19 22:27:08 +00:00
2024-05-20 16:16:32 +00:00
Logger . Write ( $"<{conn.RemoteEndPoint}> Nuked timed out connection from user #{conn.UserId}." ) ;
}
2023-02-19 22:27:08 +00:00
2024-05-19 21:02:17 +00:00
foreach ( UserInfo user in Users . All )
2024-05-20 16:16:32 +00:00
if ( ! Connections . HasUser ( user ) ) {
2024-05-29 20:51:37 +00:00
Events . Dispatch ( "user:delete" , user ) ;
2024-05-24 16:01:11 +00:00
Events . Dispatch (
"user:disconnect" ,
ChannelsUsers . GetUserLastChannel ( user ) ,
user ,
new UserDisconnectEventData ( UserDisconnectReason . TimeOut )
) ;
2023-02-19 22:27:08 +00:00
Logger . Write ( $"Timed out {user} (no more connections)." ) ;
}
}
public void SafeUpdate ( ) {
ContextAccess . Wait ( ) ;
try {
Update ( ) ;
} finally {
ContextAccess . Release ( ) ;
2023-02-16 22:33:48 +00:00
}
2022-08-30 15:00:58 +00:00
}
2024-05-23 23:13:57 +00:00
public void HandleChannelEventLog ( string channelName , Action < ISockChatS2CPacket > handler ) {
2024-05-24 03:44:20 +00:00
foreach ( ChatEventInfo info in EventStorage . GetChannelEventLog ( channelName ) ) {
switch ( info . Type ) {
2024-05-24 00:23:31 +00:00
case "msg:add" :
2024-05-24 13:11:21 +00:00
if ( info . Data is not MessageAddEventData msgAdd )
break ;
2024-05-24 00:23:31 +00:00
2024-05-24 13:11:21 +00:00
handler ( new MessageAddLogS2CPacket (
2024-05-24 03:44:20 +00:00
info . Id ,
info . Created ,
info . SenderId ,
info . SenderName ,
info . SenderColour ,
info . SenderRank ,
info . SenderPerms ,
2024-05-24 13:11:21 +00:00
msgAdd . Text ,
msgAdd . IsAction ,
2024-05-24 03:44:20 +00:00
info . ChannelName . StartsWith ( '@' ) ,
info . IsBroadcast ,
2024-05-24 00:23:31 +00:00
false
2024-05-24 13:11:21 +00:00
) ) ;
2024-05-24 00:23:31 +00:00
break ;
case "user:connect" :
2024-05-29 20:51:37 +00:00
if ( info . Data is UserConnectEventData ucData & & ucData . Notify )
handler ( new UserConnectLogS2CPacket (
info . Id ,
info . Created ,
SockChatUtility . GetUserName ( info )
) ) ;
2024-05-24 00:23:31 +00:00
break ;
case "user:disconnect" :
2024-05-24 13:11:21 +00:00
handler ( new UserDisconnectLogS2CPacket (
2024-05-24 03:44:20 +00:00
info . Id ,
info . Created ,
SockChatUtility . GetUserName ( info ) ,
info . Data is UserDisconnectEventData userDisconnect ? userDisconnect . Reason : UserDisconnectReason . Leave
2024-05-24 13:11:21 +00:00
) ) ;
2024-05-24 00:23:31 +00:00
break ;
2024-05-24 19:17:12 +00:00
case "user:update" :
if ( info . Data is UserUpdateEventData userUpdate & & userUpdate . Notify )
handler ( new UserNickChangeLogS2CPacket (
info . Id ,
info . Created ,
info . SenderNickName = = null ? info . SenderName : $"~{info.SenderNickName}" ,
userUpdate . NickName = = null ? info . SenderName : $"~{userUpdate.NickName}"
) ) ;
break ;
2024-05-24 00:23:31 +00:00
case "chan:join" :
2024-05-24 13:11:21 +00:00
handler ( new UserChannelJoinLogS2CPacket (
2024-05-24 03:44:20 +00:00
info . Id ,
info . Created ,
SockChatUtility . GetUserName ( info )
2024-05-24 13:11:21 +00:00
) ) ;
2024-05-24 00:23:31 +00:00
break ;
case "chan:leave" :
2024-05-24 13:11:21 +00:00
handler ( new UserChannelLeaveLogS2CPacket (
2024-05-24 03:44:20 +00:00
info . Id ,
info . Created ,
SockChatUtility . GetUserName ( info )
2024-05-24 13:11:21 +00:00
) ) ;
2024-05-24 03:44:20 +00:00
break ;
2024-05-24 00:23:31 +00:00
}
}
2024-05-14 22:56:56 +00:00
}
2024-05-23 23:13:57 +00:00
public void Send ( ISockChatS2CPacket packet ) {
2024-05-20 16:24:14 +00:00
string data = packet . Pack ( ) ;
2024-05-21 20:08:23 +00:00
Connections . WithAuthed ( conn = > {
if ( conn is SockChatConnectionInfo scConn )
scConn . Send ( data ) ;
} ) ;
2023-02-16 22:33:48 +00:00
}
2024-05-23 23:13:57 +00:00
public void SendTo ( UserInfo user , ISockChatS2CPacket packet ) {
2024-05-24 16:01:11 +00:00
SendTo ( user . UserId , packet . Pack ( ) ) ;
}
public void SendTo ( long userId , ISockChatS2CPacket packet ) {
SendTo ( userId , packet . Pack ( ) ) ;
}
public void SendTo ( long userId , string packet ) {
Connections . WithUser ( userId , conn = > {
2024-05-21 20:08:23 +00:00
if ( conn is SockChatConnectionInfo scConn )
2024-05-24 16:01:11 +00:00
scConn . Send ( packet ) ;
2024-05-21 20:08:23 +00:00
} ) ;
2023-02-16 22:33:48 +00:00
}
2024-05-23 23:13:57 +00:00
public void SendTo ( ChannelInfo channel , ISockChatS2CPacket packet ) {
2024-05-24 16:01:11 +00:00
SendTo ( channel . Name , packet . Pack ( ) ) ;
2024-05-20 16:24:14 +00:00
}
public void SendTo ( ChannelInfo channel , string packet ) {
2024-05-24 03:44:20 +00:00
SendTo ( channel . Name , packet ) ;
}
public void SendTo ( string channelName , ISockChatS2CPacket packet ) {
SendTo ( channelName , packet . Pack ( ) ) ;
}
public void SendTo ( string channelName , string packet ) {
long [ ] userIds = ChannelsUsers . GetChannelUserIds ( channelName ) ;
2024-05-20 16:16:32 +00:00
foreach ( long userId in userIds )
2024-05-21 20:08:23 +00:00
Connections . WithUser ( userId , conn = > {
if ( conn is SockChatConnectionInfo scConn )
scConn . Send ( packet ) ;
} ) ;
2023-02-16 22:33:48 +00:00
}
2024-05-24 19:17:12 +00:00
public void SendToUserChannels ( long userId , ISockChatS2CPacket packet ) {
ChannelInfo [ ] chans = Channels . GetMany ( ChannelsUsers . GetUserChannelNames ( userId ) ) ;
2024-05-20 16:24:14 +00:00
string data = packet . Pack ( ) ;
2024-05-19 21:02:17 +00:00
foreach ( ChannelInfo chan in chans )
2024-05-20 16:24:14 +00:00
SendTo ( chan , data ) ;
2023-02-22 00:28:53 +00:00
}
2022-08-30 15:00:58 +00:00
}
}