// Ryzom - MMORPG Framework // Copyright (C) 2010 Winch Gate Property Limited // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as // published by the Free Software Foundation, either version 3 of the // License, or (at your option) any later version. // // 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 Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . #ifndef DYN_CHAN_H #define DYN_CHAN_H #include "nel/misc/entity_id.h" #include "nel/misc/historic.h" #include "base_types.h" // forward declarations class CDynChatClient; class CDynChatChan; // identifier of a dynchat channel //typedef uint32 TChanID; typedef NLMISC::CEntityId TChanID; // tag for invalid channel const TChanID DYN_CHAT_INVALID_CHAN = NLMISC::CEntityId::Unknown; /// Message payload for player input forward to channel service owner struct TPlayerInputForward { TChanID ChanID; TDataSetRow Sender; ucstring Content; void serial(NLMISC::IStream &f) { f.serial(ChanID); f.serial(Sender); f.serial(Content); } }; //=================================================================================================================== /** This class represents an interaction between a client and a channel in a dynamic chat * A session is initiated from a CDynChat instance. */ class CDynChatSession { NL_INSTANCE_COUNTER_DECL(CDynChatSession); public: uint32 StringID; // string ID for EGS (to fill client DB) bool WriteRight; // in read-only mode client can't talk in the channel public: // Get next session for client CDynChatSession *getNextClientSession() { return _NextClientSession; } // Get next session for channel CDynChatSession *getNextChannelSession() { return _NextChannelSession; } // Get the client taking part to this session CDynChatClient *getClient() { return _Client; } // Get the channel hosting that session CDynChatChan *getChan() { return _Channel; } private: CDynChatClient *_Client; CDynChatChan *_Channel; CDynChatSession *_NextClientSession; CDynChatSession **_PrevClientSession; // Pointer on 'next' field in previous client session, or start of linked list CDynChatSession *_NextChannelSession; CDynChatSession **_PrevChannelSession; // Pointer on 'next' field in previous channel session, or start of linked list // remove the session void unlink(); // ctor CDynChatSession(CDynChatClient *client, CDynChatChan *channel); ~CDynChatSession(); friend class CDynChat; friend class CDynChatClient; friend class CDynChatChan; static uint _NumSessions; }; //=================================================================================================================== /** A client in a dyn chat system. Should be created from a CDynChat instance. */ class CDynChatClient { public: // ctor CDynChatClient(const TDataSetRow &client = TDataSetRow()); ~CDynChatClient(); // Head of linked list of sessions for this channel. List must be followed by calling CDynChatSession::getNextClientSession. CDynChatSession *getFirstSession() { return _FirstSession; } const TDataSetRow &getID() const { return _ID; } // Return session for this client in channel 'chan', or NULL if no such session exists CDynChatSession *getSession(TChanID chan) const; private: CDynChatSession *_FirstSession; TDataSetRow _ID; friend class CDynChatSession; friend class CDynChat; }; //=================================================================================================================== /** A channel in a dyn chat system. Should be created from a CDynChat instance. */ class CDynChatChan { public: struct CHistoricEntry { ucstring String; // TDataSetRow Sender; ucstring SenderString; }; NLMISC::CHistoric Historic; // historic of messages for IOS uint HistoricSize; // Historic size for EGS bool Localized; // for EGS only ucstring Title; // gives the title of the channel when it is not translated (e.g Localized == false) bool HideBubble; // hide the display of bubble public: CDynChatChan(); // CDynChatChan(TChanID id = NLMISC::CEntityId::Unknown, NLNET::TServiceId ownerServiceId, bool noBroadcast, bool forwadInput); CDynChatChan(TChanID id, bool noBroadcast, bool forwardInput, bool unified); ~CDynChatChan(); // Get ID for that channel TChanID getID() const { return _ID; } // Head of linked list of sessions for this channel. List must be followed by calling CDynChatSession::getNextChannelSession. CDynChatSession *getFirstSession() { return _FirstSession; } // Count number of session in that channel (in O(n)) uint getSessionCount() const; bool getDontBroadcastPlayerInputs() { return _DontBroadcastPlayerInputs; } bool getForwardPlayerIntputToOwnerService() { return _ForwardPlayerIntputToOwnerService;} bool getUnifiedChannel() { return _UnifyChannel;} private: CDynChatSession *_FirstSession; TChanID _ID; /// Flag to disable client broadcasting bool _DontBroadcastPlayerInputs; /// Flag to activate player input forwarding to owner service. bool _ForwardPlayerIntputToOwnerService; /// Flag to activate unification (domain wide) of this channel bool _UnifyChannel; friend class CDynChatSession; friend class CDynChat; }; //=================================================================================================================== /** Data structure to store clients / channels / sessions * To be used by both IOS & EGS * * \TODO : Would be good to generalize the pattern used as something like NLMISC::CBinaryRelation * This would avoid to have fields for one service that aren't used by an other (like the historic...) * * \author Nicolas Vizerie * \author Nevrax France * \date 2004 */ class CDynChat { private: typedef std::map TChanMap; typedef std::map TClientMap; public: // ctor CDynChat(); // dtor ~CDynChat(); /** Create a new channel with the given ID. A warning is issued if channel already exists. * \return true if creation succeeded */ bool addChan(TChanID chan, bool noBroadcast, bool forwardInput, bool unify); // Remove channel with the given ID. Return true if success bool removeChan(TChanID chan); // Add a client with the given ID. Return true if success bool addClient(const TDataSetRow &client); // Remove client from its ID. Return true if success bool removeClient(const TDataSetRow &client); /** Create a new session in channel 'chan' for the client 'client'. No-op if session already exists * \return pointer on created session */ CDynChatSession *addSession(TChanID chan, const TDataSetRow &client); // Stop session in channel 'chan' for the client 'client'. bool removeSession(TChanID chan, const TDataSetRow &client); // Get channel object from its ID or NULL if not found. CDynChatChan *getChan(TChanID chan); // Get client object from its ID or NULL if not found. CDynChatClient *getClient(const TDataSetRow &client); // Get a session or NULL if not found CDynChatSession *getSession(TChanID chan, const TDataSetRow &client); // Get channels typedef std::vector TChanPtrVector; void getChans(TChanPtrVector &channels); // Remove all channels void removeAllChannels() { _Chans.clear(); } private: // TChanMap _Chans; TClientMap _Clients; }; #endif