/*******************************************************************************
* Copyright 2014 Tobias Welther
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package de.tobiyas.racesandclasses.chat.channels;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.World;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.event.player.AsyncPlayerChatEvent;
import de.tobiyas.racesandclasses.RacesAndClasses;
import de.tobiyas.racesandclasses.chat.channels.container.ChannelContainer;
import de.tobiyas.racesandclasses.chat.channels.container.ChannelInvalidException;
import de.tobiyas.racesandclasses.chat.channels.container.ChannelTicker;
import de.tobiyas.racesandclasses.datacontainer.traitholdercontainer.AbstractTraitHolder;
import de.tobiyas.racesandclasses.playermanagement.player.RaCPlayer;
import de.tobiyas.racesandclasses.util.chat.ChannelLevel;
import de.tobiyas.racesandclasses.util.consts.Consts;
import de.tobiyas.racesandclasses.util.consts.PermissionNode;
import de.tobiyas.util.config.YAMLConfigExtended;
public class ChannelManager {
/**
* plugin main
*/
private RacesAndClasses plugin;
/**
* All channels saved as Map String -> {@link ChannelContainer}
*/
private Map<String, ChannelContainer> channels;
/**
* The Map of player -> Channel.
*/
private final Map<UUID,String> playerChannelMap = new HashMap<UUID,String>();
/**
* The ConfigTotal to save the channel settings in
*/
private YAMLConfigExtended config;
/**
* This constructor is for Testing
*
* @param config to load from and save to
*/
protected ChannelManager(YAMLConfigExtended config){
this.config = config;
this.plugin = RacesAndClasses.getPlugin();
this.channels = new HashMap<String, ChannelContainer>();
}
/**
* Creates the ChannelManager
*/
public ChannelManager(){
plugin = RacesAndClasses.getPlugin();
channels = new HashMap<String, ChannelContainer>();
config = new YAMLConfigExtended(Consts.channelsYML);
}
/**
* Inits the Channel system.
* It creates all Channels + the ticker for timings.
*/
public void init(){
channels.clear();
ChannelTicker.init();
loadChannelsFromFile();
createSTDChannels();
}
/**
* Creates all channels given by default (races / local / global)
*/
private void createSTDChannels(){
registerChannel(ChannelLevel.GlobalChannel, "Global");
registerChannel(ChannelLevel.LocalChannel, "Local");
for(World world : Bukkit.getWorlds()){
registerChannel(ChannelLevel.WorldChannel, world.getName());
}
for(String race : plugin.getRaceManager().listAllVisibleHolders()){
if(race.equalsIgnoreCase(Consts.defaultRace)) continue;
registerChannel(ChannelLevel.RaceChannel, race);
}
}
/**
* Registers a new Channel with the ChannelLevel passed
*
* @param level to create with
* @param channelName to create with
* @return the {@link ChannelContainer} created, null otherwise
*/
public ChannelContainer registerChannel(ChannelLevel level, String channelName){
ChannelContainer container = getContainer(channelName);
if(container != null){
return null;
}
try{
container = new ChannelContainer(channelName, level);
channels.put(channelName, container);
return container;
}catch(ChannelInvalidException exp){
//tried to do a RaceChannel that did not work...
return null;
}
}
/**
* Registers a Channel with a player in it.
* The player is notified to errors.
*
* @param level to register
* @param channelName to register
* @param player to pass into the channel + send errors if failed
*
* @return true if worked, false otherwise
*/
public boolean registerChannel(ChannelLevel level, String channelName, RaCPlayer player){
ChannelContainer container = registerChannel(level, channelName);
if(container == null){
if(player != null){
player.sendMessage(ChatColor.RED + "Channel: " + ChatColor.AQUA + channelName + ChatColor.RED + " already exists.");
}
return false;
}
container.addPlayerToChannel(player, "", true);
return true;
}
/**
* Registers a Channel with a player in it.
* The player is notified to errors.
*
* @param channelLevel
* @param channelName
* @param channelPassword
* @param player
*/
public boolean registerChannel(ChannelLevel channelLevel, String channelName, String channelPassword, RaCPlayer player) {
boolean worked = registerChannel(channelLevel, channelName, player);
if(worked){
ChannelContainer container = getContainer(channelName);
container.setAdmin(player);
container.setPassword(channelPassword);
if(player != null){
player.sendMessage(ChatColor.GREEN + "The channel " + ChatColor.AQUA + channelName +
ChatColor.GREEN + " has been created successfully");
}
}
return worked;
}
/**
* Creates an empty Channel file if none exists
*/
private void createStructure(){
File file = config.getFileLoadFrom();
if(!file.exists()){
try {
file.createNewFile();
} catch (IOException e) {
plugin.log("Could not create " + Consts.channelsYML);
return;
}
config = new YAMLConfigExtended(Consts.channelsYML).load();
if(!config.contains("channel")){
config.createSection("channel");
}
if(!config.contains("channel." + ChannelLevel.PasswordChannel.name())){
config.createSection("channel." + ChannelLevel.PasswordChannel.name());
}
if(!config.contains("channel." + ChannelLevel.PrivateChannel.name())){
config.createSection("channel." + ChannelLevel.PrivateChannel.name());
}
if(!config.contains("channel." + ChannelLevel.PublicChannel.name())){
config.createSection("channel." + ChannelLevel.PublicChannel.name());
}
config.save();
}
}
/**
* Loads all Channels from the Channel file
*/
private void loadChannelsFromFile(){
createStructure();
config.load();
for(ChannelLevel level : ChannelLevel.values()){
for(String channelName : config.getChildren("channel." + level.name())){
try{
ChannelContainer container = ChannelContainer.constructFromYml(config, channelName, level);
if(container.getChannelLevel() == ChannelLevel.WorldChannel && Bukkit.getWorld(container.getChannelName()) == null){
//never load worlds that do not exist any more.
continue;
}
if(container != null){
channels.put(channelName, container);
}
}catch(ChannelInvalidException exp){
continue;
}
}
}
}
/**
* Saves all channels to Channel file.
* Note: Channels can save themselves
*/
public void saveChannels(){
for(String channelName : channels.keySet()){
ChannelContainer container = getContainer(channelName);
container.saveChannel(config);
}
}
/**
* Sends an Broadcast message to the channel.
* This can be sent from an Player or from the Console (player = null)
*
* @param channel
* @param sender
* @param message
*
* @return true if worked, false if channel not found
*/
public boolean broadcastMessageToChannel(String channel, CommandSender sender,
String message) {
ChannelContainer container = getContainer(channel);
if(container == null){
if(sender != null){
sender.sendMessage(ChatColor.RED + "Channel " + ChatColor.AQUA + channel + ChatColor.RED + " was not found.");
}
return false;
}
container.sendMessageInChannel(sender, message, "");
return true;
}
/**
* Returns a list of all Channel names present
*
* @return
*/
public List<String> listAllChannels(){
ArrayList<String> channelList = new ArrayList<String>();
for(String channel : channels.keySet()){
channelList.add(channel);
}
return channelList;
}
/**
* Returns a list of all public Channel names present
*
* @return
*/
public List<String> listAllPublicChannels() {
ArrayList<String> channelList = new ArrayList<String>();
for(String channel : channels.keySet()){
ChannelContainer container = getContainer(channel);
ChannelLevel level = container.getChannelLevel();
if(level == ChannelLevel.PasswordChannel || level == ChannelLevel.PrivateChannel){
continue;
}
channelList.add(channel);
}
return channelList;
}
/**
* Posts the channel info to a player.
*
* @param sender
* @param channel
*/
public void postChannelInfo(CommandSender sender, String channel) {
ChannelContainer container = getContainer(channel);
if(container == null){
sender.sendMessage(ChatColor.RED + "Channel " + ChatColor.AQUA + channel + ChatColor.RED + " could not be found.");
return;
}
container.postInfo(sender);
}
/**
* Returns the Level of a channel according to it's name.
* Returns {@link ChannelLevel#NONE} if the channel was not found.
*
* @param channel
* @return
*/
public ChannelLevel getChannelLevel(String channel){
if(channel == null) return ChannelLevel.NONE;
for(String containerName : channels.keySet()){
if(containerName == null) continue;
ChannelContainer container = getContainer(containerName);
if(container.getChannelName().equalsIgnoreCase(channel)){
return container.getChannelLevel();
}
}
return ChannelLevel.NONE;
}
/**
* Joins the player to the channel.
* If a password is required, it is checked here.
* <br>
* If notify flag is set, other players in the channel will be informed about this.
*
* @param player to join
* @param channelName to join
* @param password needed for the channel (if it has one, ignored otherwise)
* @param notify if the player join should be notified.
*/
public void joinChannel(RaCPlayer player, String channelName, String password, boolean notify) {
ChannelContainer container = getContainer(channelName);
if(container == null) return;
container.addPlayerToChannel(player, password, notify);
}
/**
* passes the ChannelContainer associated with a channel Name.
*
* @param channelName
* @return
*/
private ChannelContainer getContainer(String channelName){
if(channelName == null) return null;
for(String name : channels.keySet()){
if(channelName.equalsIgnoreCase(name)){
return channels.get(name);
}
}
return null;
}
/**
* Removes a player from a channel.
* <br>
* If the notify flag is set, other players in the channel will be notified.
*
* @param player
* @param channelName
* @param notify
*/
public void leaveChannel(RaCPlayer player, String channelName, boolean notify) {
ChannelContainer container = getContainer(channelName);
if(container == null){
if(notify){
if(player != null) player.sendMessage(ChatColor.RED + "The Channel: " + channelName + " does not exist.");
}
return;
}
container.removePlayerFromChannel(player, notify);
}
/**
* Checks if a Player (as id) is present in a channel.
*
* @param player
* @param channelName
* @return if is present, false otherwise
*/
public boolean isMember(RaCPlayer player, String channelName) {
ChannelContainer container = getContainer(channelName);
if(container == null){
return false;
}
if(container.getChannelLevel() == ChannelLevel.LocalChannel){
return true;
}
return container.isMember(player);
}
/**
* Mutes a Player in a channel.
* The time passed is the time in Seconds the player is muted.
* <br>
* NOTE: The muting player needs Admin previleges in the channel.
*
* @param admin
* @param mutedName
* @param channelName
* @param time
*/
public void mutePlayer(RaCPlayer admin, RaCPlayer mutedRaCPlayer, String channelName, int time){
ChannelContainer container = getContainer(channelName);
if(container == null){
if(admin != null) admin.sendMessage(ChatColor.RED + "Channel not found.");
return;
}
if(!container.checkPermissionMute(admin.getPlayer())){
if(admin != null) admin.sendMessage(ChatColor.RED + "You don't have the permission to do this.");
return;
}
if(!container.isMember(mutedRaCPlayer)){
if(admin != null) admin.sendMessage(ChatColor.RED + "There is no player with this name in this channel.");
return;
}
if(container.isMuted(mutedRaCPlayer)){
if(admin != null) admin.sendMessage(ChatColor.RED + "The Player is already muted.");
return;
}
container.mutePlayer(mutedRaCPlayer, time);
broadcastMessageToChannel(channelName, null, " Player: " + mutedRaCPlayer + " got muted by: " + admin.getDisplayName());
}
/**
* Unmutes a player in a channel.
* <br>
* Note: The unmuteing player needs Admin privileges in this channel.
*
* @param admin
* @param unmutedRaCPlayer
* @param channelName
*/
public void unmutePlayer(RaCPlayer admin, RaCPlayer unmutedRaCPlayer, String channelName){
ChannelContainer container = getContainer(channelName);
if(container == null){
if(admin != null) admin.sendMessage(ChatColor.RED + "Channel not found.");
return;
}
if(!container.checkPermissionUnmute(admin.getPlayer())){
if(admin != null) admin.sendMessage(ChatColor.RED + "You don't have the permission to do this.");
return;
}
if(!container.isMember(unmutedRaCPlayer)){
if(admin != null) admin.sendMessage(ChatColor.RED + "There is no player with this name in this channel.");
return;
}
if(!container.isMuted(unmutedRaCPlayer)){
if(admin != null) admin.sendMessage(ChatColor.RED + "No player with this name is muted.");
return;
}
container.unmutePlayer(unmutedRaCPlayer);
broadcastMessageToChannel(channelName, null, " Player: " + unmutedRaCPlayer + " got unmuted by: " + admin.getDisplayName());
}
/**
* Bans a Player in a channel.
* The time passed is the time in Seconds the player is banned.
* <br>
* NOTE: The banning player needs Admin previleges in the channel.
*
* @param admin
* @param banName
* @param channelName
* @param time
*/
public void banPlayer(RaCPlayer admin, RaCPlayer banName, String channelName, int time){
ChannelContainer container = getContainer(channelName);
if(container == null){
if(admin != null) admin.sendMessage(ChatColor.RED + "Channel not found.");
return;
}
if(!container.checkPermissionBann(admin.getPlayer())){
if(admin != null) admin.sendMessage(ChatColor.RED + "You don't have the permission to do this.");
return;
}
if(!container.isMember(banName)){
if(admin != null) admin.sendMessage(ChatColor.RED + "There is no player with this name in this channel.");
return;
}
if(container.isBanned(banName)){
if(admin != null) admin.sendMessage(ChatColor.RED + "This Player is already banned from the channel.");
return;
}
broadcastMessageToChannel(channelName, null, " Player: " + banName + " got baned from channel by: " + admin.getDisplayName());
container.banAndRemovePlayer(banName, time);
}
/**
* Unbans a player in a channel.
* <br>
* Note: The unbanning player needs Admin privileges in this channel.
*
* @param admin
* @param unbanName
* @param channelName
*/
public void unbanPlayer(RaCPlayer admin, RaCPlayer unbanRaCPlayer, String channelName){
ChannelContainer container = getContainer(channelName);
if(container == null){
if(admin != null) admin.sendMessage(ChatColor.RED + "Channel not found.");
return;
}
if(!container.checkPermissionMute(admin.getPlayer())){
if(admin != null) admin.sendMessage(ChatColor.RED + "You don't have the permission to do this.");
return;
}
if(!container.isBanned(unbanRaCPlayer)){
if(admin != null) admin.sendMessage(ChatColor.RED + "No player with this name is banned.");
return;
}
container.unbanPlayer(unbanRaCPlayer);
//broadcastMessageToChannel(channelName, null, " Player: " + unbanName + " got unbaned by: " + admin.getDisplayName());
if(admin != null) admin.sendMessage(ChatColor.GREEN + "Player: " + ChatColor.LIGHT_PURPLE + unbanRaCPlayer + ChatColor.GREEN +
" got unbanned from channel: " + ChatColor.LIGHT_PURPLE + channelName);
if(unbanRaCPlayer != null){
unbanRaCPlayer.sendMessage(ChatColor.GREEN + "You got unbanned from channel: " + ChatColor.LIGHT_PURPLE + channelName);
}
}
/**
* Removes a channel from the Channel list.
*
* @param channelName2
*/
public void removeChannel(String channelName) {
ChannelContainer container = getContainer(channelName);
if(container == null){
return;
}
ChannelTicker.unregisterChannel(container);
config.load();
config.set("channel." + container.getChannelLevel() + "." + channelName, 1);
config.set("channel." + container.getChannelLevel() + "." + channelName, null);
config.save();
channels.remove(channelName);
}
/**
* Notifies all players that a player has joined
*
* @param player
*/
public void playerLogin(RaCPlayer player){
this.joinChannel(player, "Global", "", true);
this.joinChannel(player, player.getWorld().getName(), "", false);
AbstractTraitHolder container = plugin.getRaceManager().getHolderOfPlayer(player);
if(container != null){
this.joinChannel(player, container.getDisplayName(), "", false);
}
}
/**
* Notifies all players that a player has left
*
* @param player
*/
public void playerQuit(RaCPlayer player) {
this.leaveChannel(player, "Global", true);
this.leaveChannel(player, player.getWorld().getName(), false);
AbstractTraitHolder container = plugin.getRaceManager().getHolderOfPlayer(player);
if(container != null){
this.leaveChannel(player, container.getDisplayName(), false);
}
}
/**
* Changes the world channels for a player
*
* @param oldWorld
* @param player
*/
public void playerChangedWorld(World oldWorld, RaCPlayer player) {
World newWorld = player.getWorld();
boolean notify = plugin.getConfigManager().getGeneralConfig().isConfig_disableChatJoinLeaveMessages();
leaveChannel(player, oldWorld.getName(), notify);
joinChannel(player, newWorld.getName(), "", notify);
}
/**
* Changes the race channel if a player changes his race
*
* @param oldRace
* @param player
*/
public void playerLeaveRace(String oldRace, RaCPlayer player){
if(player == null) return;
AbstractTraitHolder container = plugin.getRaceManager().getHolderByName(oldRace);
if(container == null) return;
String raceToLeave = container.getDisplayName();
leaveChannel(player, raceToLeave, true);
}
/**
* Changes the race channel if a player changes his race
*
* @param oldRace
* @param player
*/
public void playerJoinRace(String newRace, RaCPlayer player){
if(player == null) return;
AbstractTraitHolder container = plugin.getRaceManager().getHolderByName(newRace);
if(container == null) return;
String raceToLeave = container.getDisplayName();
joinChannel(player, raceToLeave, "", true);
}
/**
* Edits the properties of a channel.
* <br>
* Only specific properties are present.
* Look at {@link ChannelContainer} to see the properties.
*
* @param player
* @param channel
* @param property
* @param newValue
*/
public void editChannel(RaCPlayer player, String channel, String property,
String newValue) {
ChannelContainer container = getContainer(channel);
if(container == null)
return;
container.editChannel(player, property, newValue);
}
/**
* Edits the Event to the channel passed.
*
* @param channel to edit to
* @param event to edit
*/
public void editToChannel(String channel, AsyncPlayerChatEvent event) {
//only format messages that have a message
String format = event.getFormat();
if(format.contains("%1$s") && format.contains("%2$s")){
Player sender = event.getPlayer();
ChannelContainer container = getContainer(channel);
if(container == null){
if(sender != null){
sender.sendMessage(ChatColor.RED + "Channel " + ChatColor.AQUA + channel + ChatColor.RED + " was not found.");
event.setCancelled(true);
}
return;
}
container.editEvent(event);
}
}
/**
* Deletes the Channel passed.
*
* @param deleter who wants to delete the channel
* @param channel to delete
*/
public void deleteChannel(RaCPlayer deleter, String channel) {
ChannelContainer container = getContainer(channel);
if(container.getAdmin() != deleter
|| !plugin.getPermissionManager().checkPermissions(deleter.getPlayer(), PermissionNode.channel_delete)) return;
container.sendUnformatedMessage(ChatColor.YELLOW + "Channel " + container.getChannelName() + " was deleted.");
removeChannel(container.getChannelName());
}
/**
* Returns the current Channel of the Player.
*
* @param player to use.
*
* @return the current channel.
*/
public String getCurrentChannel(RaCPlayer player){
if(!playerChannelMap.containsKey(player.getUniqueId())){
playerChannelMap.put(player.getUniqueId(), "Global");
}
return playerChannelMap.get(player.getUniqueId());
}
/**
* Changes the current channel of the player.
*
* @param player to use
* @param channel to use.
*/
public void changeCurrentChannel(RaCPlayer player, String channel){
playerChannelMap.put(player.getUniqueId(), channel);
}
}