/* * groupmanager.cpp by Anders Reggestad * * Copyright (C) 2001 Atomic Blue (info@planeshift.it, http://www.atomicblue.org) * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation (version 2 of the License) * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * */ #include #include #include #include "client.h" #include "clients.h" #include "invitemanager.h" #include "playergroup.h" #include "groupmanager.h" #include "util/log.h" #include "util/serverconsole.h" #include "util/psxmlparser.h" #include "util/eventmanager.h" #include "gem.h" #include "psserver.h" #include "globals.h" #include "chatmanager.h" /** A structure to hold the clients that are pending on joining a group. */ class PendingGroupInvite : public PendingInvite { public: int groupID; PendingGroupInvite(Client *inviter, Client *invitee, const char *question, PlayerGroup *g) : PendingInvite( inviter, invitee, true, question,"Accept","Decline", "You have invited %s to join your group.", "%s has invited you to group.", "%s has joined your group.", "You have joined the group.", "%s has declined your invitation.", "You have declined %s's invitation.", psQuestionMessage::generalConfirm) { groupID = g->id; } virtual ~PendingGroupInvite() {} void HandleAnswer(const csString & answer); }; PlayerGroup::PlayerGroup() :id(next_id++) { } PlayerGroup::PlayerGroup(GroupManager * mgr, gemActor * leader) :manager(mgr), leader(leader),id(next_id++) { members.Push(leader); leader->SetGroup(this); } PlayerGroup::~PlayerGroup() { } void PlayerGroup::Add(gemActor *new_member) { members.Push(new_member); new_member->SetGroup(this); } void PlayerGroup::Remove(gemActor *member) { psserver->SendSystemInfo(leader->GetClientID(),"%s has left the group",member->GetName()); // Delete member from group member->SetGroup(NULL); members.Delete(member); // Check if we need to find a new leader for group if (member == leader) { if ( members.Length() ) { leader = members[0]; psserver->SendSystemInfo(leader->GetClientID(), "You are now group leader."); } else { leader = 0; manager->Remove(this); return; // return immediately because our instance was deleted } } // If only one member left in the group then disband it. if ( members.Length() == 1 ) { Disband(); manager->Remove(this); return; } if (!IsEmpty()) { BroadcastMemberList(); } } void PlayerGroup::ListMembers(gemActor * client) { int clientnum = client->GetClientID(); csString str; psserver->SendSystemInfo(clientnum, "-------Group members-------"); for (size_t n = 0; n < members.Length(); n++) { str = "-"; str += members[n]->GetName(); psserver->SendSystemInfo(clientnum, str); } } void PlayerGroup::Broadcast(MsgEntry *me) { // Copy message to send out to everyone MsgEntry *newmsg = new MsgEntry(me); if (newmsg->overrun) { Bug1("Could not copy MsgEntry for PlayerGroup::Broadcast.\n"); newmsg->DecRef(); return; } newmsg->msgid = (uintptr_t) newmsg; for (size_t n = 0; n < members.Length(); n++) { newmsg->clientnum = members[n]->GetClientID(); psserver->GetEventManager()->SendMessage(newmsg); } CHECK_FINAL_DECREF(newmsg,"GroupMsg"); newmsg->DecRef(); } bool PlayerGroup::IsLeader(gemActor * client) { return (leader == client); } void PlayerGroup::Disband() { psGUIGroupMessage msg(0,psGUIGroupMessage::LEAVE,"Group Disbanded By Leader"); if (!msg.valid) { Bug1("Could not create valid psGUIGroupMessage for group.\n"); return; } for (size_t n = 0; n < members.Length(); n++) { msg.msg->clientnum = members[n]->GetClientID(); psserver->GetEventManager()->SendMessage(msg.msg); members[n]->SetGroup(NULL); } members.DeleteAll(); leader = 0; } bool PlayerGroup::IsEmpty() { return (members.Length() == 0); } float fixZero(float f) { return f == 0 ? 1 : f; } void PlayerGroup::BroadcastMemberList() { csString list; list.Append(""); for (size_t n = 0; n < members.Length(); n++) { csString buff; psCharacter *charData = members[n]->GetCharacterData(); csString escpxml = EscpXML(members[n]->GetCharacterData()->GetCharName()); buff.Format("", escpxml.GetData(), charData->AdjustHitPoints(0.0) / fixZero(charData->AdjustHitPointsMax(0.0)) * 100, charData->AdjustMana(0.0) / fixZero(charData->AdjustManaMax(0.0)) * 100, charData->GetStamina(true) / fixZero(charData->GetStaminaMax(true)) * 100, charData->GetStamina(false) / fixZero(charData->GetStaminaMax(false)) * 100); list.Append(buff); } list.Append(""); psGUIGroupMessage msg(0,psGUIGroupMessage::MEMBERS,list.GetData()); if (msg.valid) Broadcast(msg.msg); else { Bug1("Could not create valid psGUIGroupMessage for broadcast.\n"); } } bool PlayerGroup::HasMember(gemActor *member) { for (size_t i = 0; i < members.Length(); i++) if (members[i] == member) return true; return false; } //--------------------------------------------------------------------------- int PlayerGroup::next_id = 1; GroupManager::GroupManager(ClientConnectionSet *cs, ChatManager *chat) { clients = cs; chatserver = chat; // Needed to GROUPSAY things. psserver->GetEventManager()->Subscribe(this,MSGTYPE_GROUPCMD,REQUIRE_READY_CLIENT|REQUIRE_ALIVE); } GroupManager::~GroupManager() { if (psserver->GetEventManager()) psserver->GetEventManager()->Unsubscribe(this,MSGTYPE_GROUPCMD); } void GroupManager::HandleMessage(MsgEntry *me, Client *client) { psGroupCmdMessage msg(me); if (!msg.valid) { Debug2(LOG_NET,me->clientnum,"Failed to parse psGroupCmdMessage from client %u.\n",me->clientnum); return; } if (!msg.valid) { Error1("Command not supported"); return; } if (msg.command == "/invite") { Invite(msg,client); } else if (msg.command == "/groupremove") { RemovePlayerFromGroup(msg,client->GetActor()); } else if (msg.command == "/disband") { Disband(msg,client->GetActor()); } else if (msg.command == "/leavegroup") { Leave(msg,client->GetActor()); } else if (msg.command == "/groupmembers") { ListMembers(msg,client->GetActor()); } else { Error1("Command not supported by server."); } } void GroupManager::Invite(psGroupCmdMessage& msg,Client *inviter) { csRef group = inviter->GetActor()->GetGroup(); csString playerName = msg.player; //Check for empty player name. If we don't do this, the server crashes if (playerName=="") { psserver->SendSystemError(inviter->GetClientNum(), "Please specify the player name to invite to your group."); return; } playerName = NormalizeCharacterName(playerName); // Check to see if the player is trying to invite themself. if ( playerName == inviter->GetName() ) { psserver->SendSystemError(inviter->GetClientNum(), "Cannot invite yourself to a group."); return; } if (group && !group->IsLeader(inviter->GetActor())) { psserver->SendSystemError(inviter->GetClientNum(),"Only group leaders can invite new members."); return; } // invited player must be online when invited Client *invitee = clients->Find(playerName); if (!invitee) { psserver->SendSystemError(inviter->GetClientNum(),"%s is not online right now.", (const char*) playerName); return; } if (invitee->GetActor()->InGroup()) { psserver->SendSystemError(inviter->GetClientNum(),"That player is already a member of a group."); return; } if (!group) { // Create a new group with client as leader group = NewGroup(inviter->GetActor()); psserver->SendSystemInfo(inviter->GetClientNum(),"You are group leader for the new group."); } /** * Notify the inviter that the invitation has been sent. * Notify the invitee that he/she has been invited and ask them to confirm. */ psserver->SendSystemInfo(inviter->GetClientNum(),"Confirming group invitation with %s now...",invitee->GetName() ); csString invitation; invitation.Format("%s has invited you to join a group. Would you like to join it?",inviter->GetName() ); PendingGroupInvite *pnew = new PendingGroupInvite(inviter, invitee, invitation, group); // Track who is invited where, to verify confirmations psserver->questionmanager->SendQuestion(pnew); } void PendingGroupInvite::HandleAnswer(const csString & answer) { Client * client = psserver->GetConnections()->Find(clientnum); if (!client || client->GetActor()->InGroup()) return; PendingInvite::HandleAnswer(answer); if (answer == "yes") psserver->groupmanager->HandleJoinGroup(this); } void GroupManager::Disband(psGroupCmdMessage& msg,gemActor *client) { csRef group = client->GetGroup(); if (!group) { psserver->SendSystemInfo(client->GetClientID(),"You are not in a group."); return; } if (!group->IsLeader(client)) { psserver->SendSystemInfo(client->GetClientID(),"Only group leader can disband group."); return; } group->Disband(); Remove(group); psserver->SendSystemInfo(client->GetClientID(),"You have disbanded the group."); } PlayerGroup * GroupManager::FindGroup(int id) { // TODO: Change to hashmap for (size_t grpNum=0; grpNum < groups.Length(); grpNum++) if (groups[grpNum]->id == id) return groups[grpNum]; return NULL; } void GroupManager::HandleJoinGroup(PendingGroupInvite *invite) { PlayerGroup * group = FindGroup(invite->groupID); if (group == NULL) return; Client * inviteeClient = clients->Find(invite->clientnum); Client * inviterClient = clients->Find(invite->inviterClientNum); if (!inviteeClient || !AddPlayerToGroup(group,inviteeClient->GetActor())) { Error1("Error joining group!\n" ); return; } if (inviterClient != NULL) GroupChat(inviterClient->GetActor(),"Player %s has joined the group!",invite->inviteeName.GetData() ); } void GroupManager::Leave(psGroupCmdMessage& msg,gemActor *client) { csRef group = client->GetGroup(); if (!group) { psserver->SendSystemInfo(client->GetClientID(), "You are not in a group."); return; } SendLeave(client); psserver->SendSystemInfo(client->GetClientID(),"You have left the group."); // If this leave the group empty it will be deleted when going out of this scope. group->Remove(client); } void GroupManager::ListMembers(psGroupCmdMessage& msg,gemActor *client) { csRef group = client->GetGroup(); if (group) { group->ListMembers(client); } else { psserver->SendSystemInfo(client->GetClientID(),"You are not in a group" ); } } void GroupManager::SendGroup(gemActor * client) { csRef group = client->GetGroup(); csString buff; buff.Format("",group->id); psGUIGroupMessage msg(client->GetClientID(),psGUIGroupMessage::GROUP,buff); msg.SendMessage(); } void GroupManager::SendLeave(gemActor * client) { psGUIGroupMessage msg(client->GetClientID(),psGUIGroupMessage::LEAVE,""); msg.SendMessage(); } csPtr GroupManager::NewGroup(gemActor * leader) { csRef group = csPtr(new PlayerGroup(this,leader)); groups.Push(group); SendGroup(leader); group->BroadcastMemberList(); return csPtr(group); } bool GroupManager::AddPlayerToGroup(PlayerGroup * group, gemActor *client) { group->Add(client); SendGroup(client); group->BroadcastMemberList(); return true; } void GroupManager::RemovePlayerFromGroup(psGroupCmdMessage& msg,gemActor *client) { Client* targetClient; gemActor *targetClientActor; csRef group; group = client->GetGroup(); if (!group) { psserver->SendSystemInfo(client->GetClientID(), "You are not in a group."); return; } if (!msg.player || msg.player == "") { psserver->SendSystemInfo(client->GetClientID(), "You must specify the name of the member you want to remove."); return; } targetClient = clients->Find(NormalizeCharacterName(msg.player)); if (!targetClient) { psserver->SendSystemInfo(client->GetClientID(), "Cound not find a player named %s.", msg.player.GetData()); return; } if (!group->IsLeader(client)) { psserver->SendSystemInfo(client->GetClientID(), "You must be the group leader to remove members.", msg.player.GetData()); return; } targetClientActor = targetClient->GetActor(); csRef targetGroup = targetClientActor->GetGroup(); if (!targetGroup || targetGroup->id != group->id) { psserver->SendSystemInfo(client->GetClientID(), "Player %s is not a member of your group.", msg.player.GetData()); return; } SendLeave(targetClientActor); psserver->SendSystemInfo(targetClientActor->GetClientID(),"You were removed from the group."); group->Remove(targetClientActor); } void GroupManager::Remove(PlayerGroup * group) { groups.Delete(group); } void GroupManager::GroupChat(gemActor * client, const char *fmt, ...) { csString text; va_list args; va_start(args, fmt); text.FormatV(fmt,args); va_end(args); psChatMessage groupmsg(client->GetClientID(),"System",text,CHAT_GROUP, false); if (groupmsg.valid) { groupmsg.msg->Reset(); Client *c = psserver->GetConnections()->Find(client->GetClientID()); chatserver->HandleMessage(groupmsg.msg,c); } else { Bug2("Could not create a valid psChatMessage for client %u.\n",client->GetClientID()); } }