mirror of https://github.com/deavminet/dnetd.git
329 lines
6.3 KiB
D
329 lines
6.3 KiB
D
/**
|
|
* DChannel
|
|
*
|
|
* Represents a channel and its
|
|
* associated information such
|
|
* as its name, topic, members
|
|
*/
|
|
|
|
module dnetd.dchannel;
|
|
|
|
import dnetd.dconnection : DConnection;
|
|
import core.sync.mutex : Mutex;
|
|
import std.conv : to;
|
|
import std.stdio : writeln;
|
|
|
|
public class DChannel
|
|
{
|
|
/**
|
|
* Channel information
|
|
*/
|
|
private string name;
|
|
//private string topic;
|
|
|
|
/**
|
|
* Users in this channel
|
|
*/
|
|
private DConnection[] members;
|
|
private Mutex memberLock;
|
|
|
|
this(string name)
|
|
{
|
|
/* Initialize the lock */
|
|
memberLock = new Mutex();
|
|
|
|
this.name = name;
|
|
}
|
|
|
|
public string getName()
|
|
{
|
|
return name;
|
|
}
|
|
|
|
/**
|
|
* Joins the given client to this channel
|
|
*/
|
|
public bool join(DConnection client)
|
|
{
|
|
/* Send a message stating the user has joined (TODO: This should be done later, possibly, how defensive should we program) */
|
|
broadcastJoin(client);
|
|
|
|
/* Lock the members list */
|
|
writeln("join: mutex lock (about to call)");
|
|
memberLock.lock();
|
|
writeln("join: mutex lock (completed)");
|
|
|
|
/**
|
|
* Don't allow the user to join a channel he
|
|
* is already in
|
|
*/
|
|
bool isPresent = false;
|
|
|
|
foreach(DConnection member; members)
|
|
{
|
|
if(client is member)
|
|
{
|
|
isPresent = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* TODO: Error handling if the calling DConnection fails midway
|
|
* and doesn't unlock it
|
|
*/
|
|
|
|
/* Only join channel if not already joined */
|
|
if(!isPresent)
|
|
{
|
|
/* Add the client */
|
|
members ~= client;
|
|
}
|
|
|
|
/* Unlock the members list */
|
|
writeln("join: mutex unlock (about to call)");
|
|
memberLock.unlock();
|
|
writeln("join: mutex unlock (completed)");
|
|
|
|
return isPresent;
|
|
}
|
|
|
|
/**
|
|
* Returns the number of members in this channel
|
|
*/
|
|
public ulong getMemberCount()
|
|
{
|
|
/* The count of members */
|
|
ulong memberCount;
|
|
|
|
/* Lock the members list */
|
|
memberLock.lock();
|
|
|
|
/* Get the member count */
|
|
memberCount = members.length;
|
|
|
|
/* Unlock the members list */
|
|
memberLock.unlock();
|
|
|
|
return memberCount;
|
|
}
|
|
|
|
public bool isMember(DConnection client)
|
|
{
|
|
/* Whether or not you are a member */
|
|
bool isMem;
|
|
|
|
/* Lock the members list */
|
|
memberLock.lock();
|
|
|
|
/* CHeck if you are in this channel */
|
|
foreach(DConnection member; members)
|
|
{
|
|
if(member is client)
|
|
{
|
|
isMem = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Unlock the members list */
|
|
memberLock.unlock();
|
|
|
|
return isMem;
|
|
}
|
|
|
|
/**
|
|
* Removes the given client from this channel
|
|
*/
|
|
public void leave(DConnection client)
|
|
{
|
|
/* Lock the members list */
|
|
memberLock.lock();
|
|
|
|
/* TODO: Get a better implementation */
|
|
|
|
/* Create a new list without the `client` */
|
|
DConnection[] newMembers;
|
|
foreach(DConnection currentMember; members)
|
|
{
|
|
if(!(currentMember is client))
|
|
{
|
|
newMembers ~= currentMember;
|
|
}
|
|
}
|
|
|
|
/* Set it as the new list */
|
|
members = newMembers;
|
|
|
|
/* Unlock the members list */
|
|
memberLock.unlock();
|
|
|
|
/* Send broadcast leave message */
|
|
broadcastLeave(client);
|
|
}
|
|
|
|
/**
|
|
* Sends a message to all users of this
|
|
* channel that the given user has left
|
|
*/
|
|
private void broadcastLeave(DConnection left)
|
|
{
|
|
/* Lock the members list */
|
|
memberLock.lock();
|
|
|
|
/* Send left message here */
|
|
foreach(DConnection currentMember; members)
|
|
{
|
|
sendLeaveMessage(currentMember, left);
|
|
}
|
|
|
|
/* Unlock the members list */
|
|
memberLock.unlock();
|
|
}
|
|
|
|
/**
|
|
* Sends a message to the user stating the given
|
|
* (other) user has left the channel
|
|
*/
|
|
private void sendLeaveMessage(DConnection member, DConnection left)
|
|
{
|
|
/* The protocol data to send */
|
|
byte[] protocolData;
|
|
|
|
/* Set the notificaiton type to `channel status` */
|
|
protocolData ~= [1];
|
|
|
|
/* Set the sub-type to leave */
|
|
protocolData ~= [0];
|
|
|
|
/* Set the channel notificaiton type to `member leave` */
|
|
protocolData ~= cast(byte[])left.getUsername();
|
|
|
|
/* Write the notification */
|
|
member.writeSocket(0, protocolData);
|
|
}
|
|
|
|
/**
|
|
* Sends a message to all users of this
|
|
* channel that the given user has joined
|
|
*/
|
|
private void broadcastJoin(DConnection joined)
|
|
{
|
|
/* Lock the members list */
|
|
memberLock.lock();
|
|
|
|
/* Send join message here */
|
|
foreach(DConnection currentMember; members)
|
|
{
|
|
sendJoinMessage(currentMember, joined);
|
|
}
|
|
|
|
/* Unlock the members list */
|
|
memberLock.unlock();
|
|
}
|
|
|
|
/**
|
|
* Sends a message to the user stating the given
|
|
* (other) user has joined the channel
|
|
*/
|
|
private void sendJoinMessage(DConnection member, DConnection joined)
|
|
{
|
|
/* The protocol data to send */
|
|
byte[] protocolData;
|
|
|
|
/* Set the notificaiton type to `channel status` */
|
|
protocolData ~= [1];
|
|
|
|
/* Set the sub-type to join */
|
|
protocolData ~= [1];
|
|
|
|
/* Set the channel notificaiton type to `member join` */
|
|
protocolData ~= cast(byte[])joined.getUsername();
|
|
|
|
/* Write the notification */
|
|
member.writeSocket(0, protocolData);
|
|
}
|
|
|
|
|
|
|
|
public bool sendMessage(DConnection sender, string message)
|
|
{
|
|
bool status;
|
|
|
|
/* The protocol data to send */
|
|
byte[] msg;
|
|
|
|
/* Set the notificaiton type to `message notification` */
|
|
msg ~= [0];
|
|
|
|
/**
|
|
* Format
|
|
* 0 - dm
|
|
* 1 - channel (this case)
|
|
* byte length of name of channel/person (dm case)
|
|
* message-bytes
|
|
*/
|
|
msg ~= [cast(byte)1,(cast(byte)sender.getUsername().length)]~cast(byte[])sender.getUsername()~cast(byte[])message;
|
|
|
|
/* Send the message to everyone else in the channel */
|
|
foreach(DConnection member; members)
|
|
{
|
|
/* Skip sending to self */
|
|
if(!(member is sender))
|
|
{
|
|
/* Send the message */
|
|
writeln("Delivering message '"~message~"' for channel '"~name~"' to user '"~member.getUsername()~"'...");
|
|
status = member.writeSocket(0, msg);
|
|
|
|
if(status)
|
|
{
|
|
writeln("Delivered message '"~message~"' for channel '"~name~"' to user '"~member.getUsername()~"'!");
|
|
}
|
|
else
|
|
{
|
|
writeln("Failed to deliver message '"~message~"' for channel '"~name~"' to user '"~member.getUsername()~"'!");
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/* TODO: Don't, retur true */
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Returns a list of all the members
|
|
*/
|
|
public DConnection[] getMembers()
|
|
{
|
|
/* Members list */
|
|
DConnection[] memberList;
|
|
|
|
/* Lock the members list */
|
|
memberLock.lock();
|
|
|
|
memberList = members;
|
|
|
|
/* Unlock the members list */
|
|
memberLock.unlock();
|
|
|
|
return memberList;
|
|
}
|
|
|
|
|
|
public override string toString()
|
|
{
|
|
string toStr;
|
|
|
|
/* Lock the members list */
|
|
memberLock.lock();
|
|
|
|
toStr = "DChannel [Name: "~name~", Members: "~to!(string)(members)~"]";
|
|
|
|
/* Unlock the members list */
|
|
memberLock.unlock();
|
|
|
|
return toStr;
|
|
}
|
|
|
|
} |