• Unfortunately, we have experienced significant hard drive damage that requires urgent maintenance and rebuilding. The forum will be a state of read only until we install our new drives and rebuild all the configurations needed. Please follow our Facebook page for updates, we will be back up shortly! (The forum could go offline at any given time due to the nature of the failed drives whilst awaiting the upgrades.) When you see an Incapsula error, you know we are in the process of migration.

Advanced Channel Chat System [ WoW-like ] + Ideas

Custom Title Activated
Loyal Member
Joined
Apr 29, 2008
Messages
1,297
Reaction score
509
Since I've practically stopped Maple Dev for a long time. I guess I'll just release stuff that I made for my server a long time ago.

This system is basically similar to how you would chat in WoW. There are a lot of other things you could add / change. It isn't completely done, but it should be working just fine. This was made few years back. So if you think it was badly coded, fix it yourself.

Required to add this :
  • Basic / Simple Java Knowledge

What this does
It basically lets user join channels, and talk among the people in those channels.

How do they talk?
They could either use a command to set their current channel, which means everything they typed in the 'To All' chat will get sent to the channel instead. Or they could do @<ch no> <message>

How to join channels?
Simply type @join <channel name>

Example of a message sent?
[1 - Slackers]Xerixe: Hi there
[3 - BPQ]Noob: LFP

Notes
Users can join multiple channels. First person in the channel becomes the owner.

P/S : Released this because of AngryPepe's Chat System. Yea, I dislike the way he named every poop pepe. So this well, if you could add it properly, makes that system obsolete.

If you have no knowledge at all in Java / know how to edit things, adding this would cause a lot of errors.

Here's the list of things that I was going to add, but didn't have the time to. These are ideas so you can customize it more.
The stuff that I was going to implement :
  • Moderators ( Level System )
  • Saving Channels to Database ( Something like IRC )
  • Proper Channel Command Handling ( Instead of @<ch no> <command>, @<command> and it uses it on the current channel )
  • Preset Channels for users to join ( Like WoW )

ChatChannel
Code:
package server;

import client.MapleCharacter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 *
 * @author Xerixe
 *
 */
public class ChatChannel {

	private String name, owner;
	private List<MapleCharacter> members = new ArrayList<MapleCharacter>();
	private static Map<String, ChatChannel> channels = new HashMap<String, ChatChannel>();
	private List<String> banned = new ArrayList<String>();
	private static ReentrantReadWriteLock channelLock = new ReentrantReadWriteLock();
	private ReentrantReadWriteLock memberLock = new ReentrantReadWriteLock();

	public ChatChannel(String name, String owner) {
		this.name = name;
		this.owner = owner;
	}

	public boolean isOwner(String name) {
		return owner.equalsIgnoreCase(name);
	}

	public String getName() {
		return name;
	}

	public boolean addMember(MapleCharacter chr) {
		if (banned.contains(chr.getName()) && !chr.isGM()) {
			return false;
		}
		memberLock.writeLock().lock();
		try {
			members.add(chr);
		} finally {
			memberLock.writeLock().unlock();
		}
		return true;
	}

	public void removeMember(MapleCharacter chr) {
		memberLock.writeLock().lock();
		try {
			members.remove(chr);
		} finally {
			memberLock.writeLock().unlock();
		}
	}

	public void removeMember(MapleCharacter chr, boolean kicked) {
		chr.dropMessage(5, getPreset(chr) + "You have " + (kicked ? "been kicked from " : "left ") + name);
		removeMember(chr);
	}

	public static void addChannel(ChatChannel cc) {
		channelLock.writeLock().lock();
		try {
			channels.put(cc.getName(), cc);
		} finally {
			channelLock.writeLock().unlock();
		}
	}

	public static void removeChannel(String name) {
		channelLock.writeLock().lock();
		try {
			channels.remove(name);
		} finally {
			channelLock.writeLock().unlock();
		}
	}

	public void message(String from, String message) {
		memberLock.readLock().lock();
		try {
			for (MapleCharacter chr : members) {
				if (chr != null) {
					chr.dropMessage(5, getPreset(chr) + from + ": " + message);
				}
			}
		} finally {
			memberLock.readLock().unlock();
		}
	}

	private String getPreset(MapleCharacter chr) {
		return "[" + chr.getChannelNo(name) + " - " + name + "]";
	}

	public static ChatChannel getChannel(String name) {
		channelLock.readLock().lock();
		try {
			return channels.get(name);
		} finally {
			channelLock.readLock().unlock();
		}
	}

	public static boolean joinChannel(MapleCharacter chr, String ch) {
		channelLock.readLock().lock();
		try {
			if (channels.containsKey(ch)) {
				return getChannel(ch).addMember(chr);
			} else {
				ChatChannel cc = new ChatChannel(ch, chr.getName());
				addChannel(cc);
				return cc.addMember(chr);
			}
		} finally {
			channelLock.readLock().unlock();
		}

	}

	public boolean hasMember(String name) {
		memberLock.readLock().lock();
		try {
			for (MapleCharacter chr : members) {
				if (chr.getName().equalsIgnoreCase(name)) {
					return true;
				}
			}
		} finally {
			memberLock.readLock().unlock();
		}
		return false;
	}

	public void kick(MapleCharacter c, String name) {
		boolean kick = false;
		memberLock.readLock().lock();
		try {
			for (MapleCharacter chr : members) {
				if (chr.getName().equalsIgnoreCase(name)) {
					if (chr.getGMLevel() > c.getGMLevel() && c.getGMLevel() < 5) {
						c.dropMessage(getPreset(c) + "Unable to kick player.");
					} else {
						kick = true;
						c.dropMessage(getPreset(c) + "The user has been kicked from " + name);
						banned.add(name);
					}
				}
			}
		} finally {
			memberLock.readLock().unlock();
		}
		if (kick) {
			c.leaveChannel(this.name, true);
		}
	}

	public boolean isBanned(String name) {
		return banned.contains(name);
	}

	public void unban(String name) {
		banned.remove(name);
	}
}

MapleCharacter
Code:
private int currentChannel = -1;
private Map<Integer, String> channels = new HashMap<Integer, String>();
Code:
public void joinChannel(String name) {
		int newChannel = 0;
		for (Integer i : channels.keySet()) {
			if (i > newChannel) {
				newChannel = i;
			}
		}
		int ch = newChannel + 1;
		channels.put(ch, name);
		dropMessage(5, "You have joined " + name + " [" + ch + "]. To send a message, type @" + ch + " <message>. "
				+ "Or use @setchannel " + ch + " to set your current channel, and talk like normal. Type @setchannel to resume normal 'To All' chat");
	}

	public int getCurrentChannel() {
		return currentChannel;
	}

	public void setCurrentChannel(int channel) {
		currentChannel = channel;
	}
	
	public void leaveChannel(int channel) {
		if (channels.containsKey(channel)) {
			ChatChannel.getChannel(getChannelName(channel)).removeMember(this, false);
		}
	}
	
	public void leaveChannel(String name, boolean kicked) {
		if (channels.containsValue(name)) {
			ChatChannel.getChannel(name).removeMember(this, kicked);
			channels.remove(getChannelNo(name));
		}
	}

	public int getChannelNo(String name) {
		for (Entry<Integer, String> chs : channels.entrySet()) {
			if (chs.getValue().equalsIgnoreCase(name)) {
				return chs.getKey();
			}
		}
		return -1;
	}

	public boolean inChannel(int ch) {
		return channels.containsKey(ch);
	}

	public boolean inChannel(String cc) {
		return channels.containsValue(cc);
	}

	public String getChannelName(int ch) {
		return channels.get(ch);
	}

	public ChatChannel getCChannel(int no) {
		return ChatChannel.getChannel(getChannelName(no));
	}

	public Map<Integer, String> getChannels() {
		return channels;
	}
In the logout / empty function / somewhere
Code:
for (Entry<Integer, String> ch : channels.entrySet()) {
			ChatChannel cc = ChatChannel.getChannel(ch.getValue());
			if (cc != null) {
				cc.removeMember(this);
			}
		}
.reconstructCharacter
Code:
for (Entry<Integer, String> ch : ct.channels.entrySet()) {
			ret.channels.put(ch.getKey(), ch.getValue());
		}

Your player commands
Code:
if (splitted[0].equalsIgnoreCase("join")) {
			String channel = StringUtil.joinStringFrom(splitted, 1);
			if (c.getPlayer().getChannelNo(channel) == -1) {
				if (ChatChannel.joinChannel(c.getPlayer(), channel)) {
					c.getPlayer().joinChannel(channel);
				}
			} else {
				c.getPlayer().dropMessage("You are already in that channel!");
			}
		} else if (splitted[0].equalsIgnoreCase("leave")) {
			String channel = StringUtil.joinStringFrom(splitted, 1);
			int channelNo = c.getPlayer().getChannelNo(channel);
			if (channelNo != -1) {
				c.getPlayer().leaveChannel(channelNo);
			} else {
				try {
					channelNo = Integer.parseInt(channel);
					if (c.getPlayer().inChannel(channelNo)) {
						c.getPlayer().leaveChannel(channelNo);
					}
				} catch (NumberFormatException e) {
					c.getPlayer().dropMessage("You may not leave a channel which you are not in!");
				}
			}
		} else if (splitted[0].equalsIgnoreCase("setchannel")) {
			if (splitted.length > 1) {
				String channel = StringUtil.joinStringFrom(splitted, 1);
				int channelNo = c.getPlayer().getChannelNo(channel);
				if (channelNo != -1) {
					c.getPlayer().setCurrentChannel(channelNo);
				} else {
					try {
						channelNo = Integer.parseInt(channel);
						if (c.getPlayer().inChannel(channelNo)) {
							c.getPlayer().setCurrentChannel(channelNo);
						}
					} catch (NumberFormatException e) {
						c.getPlayer().dropMessage("You have not joined that channel!");
					}
				}
			} else {
				c.getPlayer().setCurrentChannel(-1);
			}
		}

ChannelCommand.java
Code:
package client.messages.commands;

import client.MapleCharacter;
import client.MapleClient;
import server.ChatChannel;
import tools.StringUtil;

/**
 *
 * @author Xerixe
 *
 */
public class ChannelCommands {
	
	public static boolean execute(MapleClient c, String[] splitted) throws Exception {
		MapleCharacter chr = c.getPlayer();
		try {
			if (!chr.inChannel(Integer.parseInt(splitted[0]))) {
				return false;
			}
		} catch (NumberFormatException e) {
			return false;
		}
		ChatChannel cc = chr.getCChannel(Integer.parseInt(splitted[0]));
		if (cc == null) {
			return false;
		}
		if (cc.isOwner(chr.getName())) {
			if (splitted[1].equalsIgnoreCase("kick")) {
				if (cc.hasMember(splitted[2])) {
					cc.kick(chr, splitted[2]);
				} else {
					chr.dropMessage("[Chat]Player not found!");
				}
			} else if (splitted[1].equalsIgnoreCase("ban")) {
				if (cc.hasMember(splitted[2])) {
					cc.kick(chr, splitted[2]);
				} else {
					chr.dropMessage("[Chat]Player not found!");
				}
			} else if (splitted[1].equalsIgnoreCase("invite")) {

			} else if (splitted[1].equalsIgnoreCase("unban")) {
				if (cc.isBanned(splitted[2])) {
					cc.unban(splitted[2]);
					chr.dropMessage("[Chat]Player is unbanned!");
				} else {
					chr.dropMessage("[Chat]Player is not banned!");
				}
			} else if (splitted[1].equalsIgnoreCase("changeowner")) {

			} else {
				cc.message(chr.getName(), StringUtil.joinStringFrom(splitted, 1));
			}
		} else {
			cc.message(chr.getName(), StringUtil.joinStringFrom(splitted, 1));
		}
		return true;
	}
}

CommandProcessor.processCommandInternal, put this somewhere near the bottom
Code:
try {
				if (ChannelCommands.execute(c, splitted))
					return true;
			} catch (Exception e) {}

In GeneralChatHandler / ChatHandler.generalChat / etc.
Change it from
Code:
    broadcastMessage(getChatText) part
To
Code:
if (c.getPlayer().getCurrentChannel() != -1) {
					ChatChannel cc = c.getPlayer().getCChannel(Integer.parseInt(text));
					cc.message(c.getPlayer().getName(), text);
				} else {
					broadcastMessage(getChatText) part
				}

CharacterTransfer
Code:
public final Map<Integer, String> channels = new TreeMap<Integer, String>();
CharacterTransfer(MapleCharacter chr)
Code:
for (Entry<Integer, String> ch : chr.getChannels().entrySet()) {
			channels.put(ch.getKey(), ch.getValue());
		}
.readExternal ( Before transfertime? )
Code:
int size = in.readByte();
		for (int i = 0; i < size; i++) {
			int no = in.readByte();
			String ch = (String) in.readObject();
			channels.put(no, ch);
		}
.writeExternal ( At the end )
Code:
out.writeByte(channels.size());
		for (int i = 0; i < channels.size(); i++) {
			for (Entry<Integer, String> ch : channels.entrySet()) {
				out.writeByte(ch.getKey());
				out.writeObject(ch.getValue());
			}
		}

I think that's it. Tell me if I missed out anything tho.
 
Last edited:
Junior Spellweaver
Joined
Apr 9, 2012
Messages
117
Reaction score
28
Nice job, although it's always recommended to show pictures/video of the system.
 
Custom Title Activated
Loyal Member
Joined
Apr 29, 2008
Messages
1,297
Reaction score
509
Nice job, although it's always recommended to show pictures/video of the system.

Screenies are mostly for newbies / leechers. So I don't see a point in it.
Also, I don't have Maple on my computer anymore.
 
warp(california, "home");
Joined
Sep 16, 2008
Messages
294
Reaction score
103
Great work. However, this does render smegas useless...
 
Custom Title Activated
Loyal Member
Joined
Apr 29, 2008
Messages
1,297
Reaction score
509
Great work. However, this does render smegas useless...

Not really. It would just reduce the spams on smegas. I mean, it's not like everyone on the server would join a channel. Smegas are more to server wide, no?
 
Back
Top