1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010 Winch Gate Property Limited
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU Affero General Public License as
6 // published by the Free Software Foundation, either version 3 of the
7 // License, or (at your option) any later version.
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU Affero General Public License for more details.
14 // You should have received a copy of the GNU Affero General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
17 //-----------------------------------------------------------------------------
19 //-----------------------------------------------------------------------------
22 #include "nel/misc/command.h"
23 #include "nel/net/message.h"
24 #include "nel/net/unified_network.h"
25 #include "nel/net/service.h"
27 #include "game_share/singleton_registry.h"
28 #include "game_share/dyn_chat.h"
29 #include "game_share/ryzom_entity_id.h"
30 #include "game_share/synchronised_message.h"
31 #include "game_share/ios_interface.h"
33 #include "gus_mirror.h"
34 #include "gus_client_manager.h"
37 //-----------------------------------------------------------------------------
39 //-----------------------------------------------------------------------------
42 using namespace NLMISC
;
43 using namespace NLNET
;
46 //-----------------------------------------------------------------------------
48 //-----------------------------------------------------------------------------
52 //-----------------------------------------------------------------------------
53 // class CChatChannelImplementation
54 //-----------------------------------------------------------------------------
56 class CChatChannelImplementation
: public CChatChannel
58 friend class CChatManagerImplementation
;
60 CChatChannelImplementation(const NLMISC::CSString
&channelName
);
62 void openChannel(const NLMISC::CSString
& channelTitle
, uint32 historySize
, bool noBroadcast
, bool forwardInput
, bool autoInsertPlayer
);
65 void addClient(GUS::TClientId clientId
);
66 void removeClient(GUS::TClientId clientId
);
68 void broadcastMessage(const ucstring
& speakerName
, const ucstring
& txt
);
69 void broadcastMessage(const std::string
& speakerNameUtf8
, const std::string
& txtUtf8
);
70 void sendMessage(GUS::TClientId clientId
, const ucstring
& speakerName
,const ucstring
& txt
);
71 void sendMessage(GUS::TClientId clientId
, const std::string
& speakerNameUtf8
, const std::string
& txtUtf8
);
73 void setChatCallback(IChatCallback
*callback
);
74 const NLMISC::CSString
& getChannelName() const;
75 const NLMISC::CSString
& getChannelTitle() const;
76 void setChannelTitle(const NLMISC::CSString
& title
);
80 IChatCallback
* _ChatCallback
;
82 NLMISC::CSString _ChannelName
;
85 NLMISC::CSString _ChannelTitle
;
89 bool _AutoInsertPlayer
;
91 struct TClientValidation
93 GUS::TClientId ClientId
;
96 // List of client waiting 'validation' in the chat
97 list
<TClientValidation
> _ClientToValidate
;
99 NLMISC_COMMAND_FRIEND(chatDisplayChannel
);
100 NLMISC_COMMAND_FRIEND(chatPlayerInput
);
104 //-----------------------------------------------------------------------------
105 // class CChatManagerImplementation
106 //-----------------------------------------------------------------------------
108 class CChatManagerImplementation
109 : public CChatManager
,
110 public CGusMirror::IMirrorModuleCallback
,
111 public CClientManager::IConnectionHandler
,
112 public IServiceSingleton
115 // prohibit instantiation with private ctor
116 CChatManagerImplementation();
118 //-----------------------------------------------------------------------------
119 // specialisation of CGusMirror::IMirrorModuleCallback
121 void mirrorIsReady(CGusMirror
*mirrorModule
) {};
122 void serviceMirrorUp(CGusMirror
*mirrorModule
, const std::string
&serviceName
, NLNET::TServiceId serviceId
);
123 void serviceMirrorDown(CGusMirror
*mirrorModule
, const std::string
&serviceName
, NLNET::TServiceId serviceId
) {};
124 void mirrorTickUpdate(CGusMirror
*mirrorModule
);
125 void entityAdded(CGusMirror
*mirrorModule
, CMirroredDataSet
*dataSet
, const TDataSetRow
&entityIndex
) {};
126 void entityRemoved(CGusMirror
*mirrorModule
, CMirroredDataSet
*dataSet
, const TDataSetRow
&entityIndex
, const NLMISC::CEntityId
*entityId
) {};
127 void propertyChanged(CGusMirror
*mirrorModule
, CMirroredDataSet
*dataSet
, const TDataSetRow
&entityIndex
, TPropertyIndex propIndex
) {};
130 //-----------------------------------------------------------------------------
131 // specialisation of CClientManager::IConnectionHandler
133 void connect(TClientId
);
134 void disconnect(TClientId
);
137 //-----------------------------------------------------------------------------
138 // specialisation of IServiceSingleton
144 //-----------------------------------------------------------------------------
145 // specialisation of CChatManager
147 TChatChannelPtr
createChatChannel(const NLMISC::CSString
&channelName
);
148 void removeChannel(CChatChannel
*channel
);
149 TChatChannelPtr
getChatChannel(const NLMISC::CSString
& channelName
);
150 bool setChatChannelTitle(CChatChannel
*channel
,const NLMISC::CSString
& channelTitle
);
153 //-----------------------------------------------------------------------------
154 // propriatory public interface
156 // get singleton instance
157 static CChatManagerImplementation
* getInstance();
159 uint32
generateChannelNumber();
161 static void cbDynChatForward( CMessage
& msgin
, const string
&serviceName
, NLNET::TServiceId serviceId
);
164 //-----------------------------------------------------------------------------
167 typedef map
<CSString
, CSmartPtr
<CChatChannelImplementation
> > TChannels
;
170 /// Channel numbering (incremented for each channel created)
171 uint32 _CurrentChannelNumber
;
174 //-----------------------------------------------------------------------------
175 // friendship for associated NLMISC_COMMANDs
177 NLMISC_COMMAND_FRIEND(chatCreateChannel
);
178 NLMISC_COMMAND_FRIEND(chatRemoveChannel
);
179 NLMISC_COMMAND_FRIEND(chatSendMessage
);
180 NLMISC_COMMAND_FRIEND(chatDisplayChannel
);
181 NLMISC_COMMAND_FRIEND(chatPlayerInput
);
184 //-----------------------------------------------------------------------------
185 // NeL service callbacks
186 //-----------------------------------------------------------------------------
187 TUnifiedCallbackItem ChatCbArray
[]=
189 { "DYN_CHAT:FORWARD", CChatManagerImplementation::cbDynChatForward
},
192 //-----------------------------------------------------------------------------
193 // methods CChatManagerImplementation
194 //-----------------------------------------------------------------------------
196 CChatManagerImplementation::CChatManagerImplementation()
197 : _CurrentChannelNumber(0)
201 void CChatManagerImplementation::serviceMirrorUp(CGusMirror
*mirrorModule
, const std::string
&serviceName
, NLNET::TServiceId serviceId
)
203 if (serviceName
== "EGS")
205 // open all channel on the EGS server
206 TChannels::iterator
it(_Channels
.begin()), last(_Channels
.end());
207 for(; it
!= last
; ++it
)
209 CChatChannelImplementation
*cci
= it
->second
;
211 cci
->openChannel(cci
->_ChannelTitle
, cci
->_HistorySize
, cci
->_NoBroadcast
, cci
->_ForwardInput
, cci
->_AutoInsertPlayer
);
216 void CChatManagerImplementation::mirrorTickUpdate(CGusMirror
*mirrorModule
)
218 // for each channel, update waiting clients
220 TGameCycle now
= CTickEventHandler::getGameCycle();
222 TChannels::iterator
it(_Channels
.begin()), last(_Channels
.end());
223 for (; it
!= last
; ++it
)
225 CChatChannelImplementation
*cci
= it
->second
;
227 while( !cci
->_ClientToValidate
.empty()
228 && (cci
->_ClientToValidate
.front().EntryDate
< (now
- 50)))
230 if (cci
->_ChatCallback
!= NULL
)
231 cci
->_ChatCallback
->clientReadyInChannel(cci
, cci
->_ClientToValidate
.front().ClientId
);
233 cci
->_ClientToValidate
.pop_front();
239 void CChatManagerImplementation::connect(TClientId client
)
241 // For now, we consider connecting all client in all chat channel
242 TChannels::iterator
it(_Channels
.begin()), last(_Channels
.end());
243 for (; it
!= last
; ++it
)
245 CChatChannelImplementation
*cci
= it
->second
;
247 if (cci
->_AutoInsertPlayer
||
248 (cci
->_ChatCallback
!= NULL
&& cci
->_ChatCallback
->isClientAllowedInChatChannel(client
, cci
)))
250 cci
->addClient(client
);
251 // // add the client into the chat
252 // TChanID chan = cci->_ChannelID;
253 // bool writeRight = true;
255 // CMessage msgout("DYN_CHAT:ADD_SESSION");
256 // msgout.serial(chan);
257 // msgout.serial(client);
258 // msgout.serial(writeRight);
260 // sendMessageViaMirror( "EGS", msgout);
262 // CChatChannelImplementation::TClientValidation cv;
263 // cv.ClientId = client;
264 // cv.EntryDate = CTickEventHandler::getGameCycle();
266 // cci->_ClientToValidate.push_back(cv);
271 void CChatManagerImplementation::disconnect(TClientId
)
273 // Nothing special to do
276 void CChatManagerImplementation::init()
278 NLNET::CUnifiedNetwork::getInstance()->addCallbackArray(ChatCbArray
, sizeof(ChatCbArray
) / sizeof(TUnifiedCallbackItem
));
280 // Register a callback in client manager
281 CClientManager::getInstance()->setConnectionCallback(this);
283 // Register a callback in the gus mirror
284 CGusMirror::getInstance()->registerModuleCallback(this);
287 CChatManagerImplementation
* CChatManagerImplementation::getInstance()
289 static CChatManagerImplementation
* ptr
=NULL
;
291 ptr
= new CChatManagerImplementation
;
295 TChatChannelPtr
CChatManagerImplementation::createChatChannel(const NLMISC::CSString
&channelName
)
297 if (_Channels
.find(channelName
) != _Channels
.end())
299 nlwarning("A channel with name '%s' already exist, creation failed", channelName
.c_str());
302 CChatChannelImplementation
*cc
= new CChatChannelImplementation(channelName
);
303 _Channels
[channelName
] = cc
;
308 void CChatManagerImplementation::removeChannel(CChatChannel
*channel
)
310 TChannels::iterator
it(_Channels
.begin()), last(_Channels
.end());
312 for (; it
!= last
; ++it
)
314 if (it
->second
== (CChatChannelImplementation
*)channel
)
322 TChatChannelPtr
CChatManagerImplementation::getChatChannel(const NLMISC::CSString
& channelName
)
324 if (_Channels
.find(channelName
) == _Channels
.end())
326 nlwarning("Chat Channel '%s' is unknown", channelName
.c_str());
327 return TChatChannelPtr();
330 return &*_Channels
[channelName
];
333 bool CChatManagerImplementation::setChatChannelTitle(CChatChannel
*channel
,const NLMISC::CSString
& channelTitle
)
336 CSString title
= channelTitle
;
338 TChannels::iterator
it(_Channels
.begin()), last(_Channels
.end());
339 for (; it
!= last
; ++it
)
341 if (it
->second
!=NULL
&& it
->second
!= (CChatChannelImplementation
*)channel
&&
342 (it
->second
->getChannelTitle()==title
|| it
->second
->getChannelName()==title
) )
344 // the channel title is already assigned to another channel
346 title
= channel
->getChannelName();
350 channel
->setChannelTitle(title
);
354 uint32
CChatManagerImplementation::generateChannelNumber()
356 return _CurrentChannelNumber
++;
360 void CChatManagerImplementation::cbDynChatForward( CMessage
& msgin
, const string
&serviceName
, NLNET::TServiceId serviceId
)
362 CChatManagerImplementation
*cmi
= CChatManagerImplementation::getInstance();
364 // serial the player inputs
365 TPlayerInputForward pif
;
368 CEntityId eid
= CGusMirror::getInstance()->getDataSet("fe_temp")->getEntityId(pif
.Sender
);
370 nldebug("Receiving input '%s' from player %s %s",
371 pif
.Content
.toString().c_str(),
372 eid
.toString().c_str(),
373 pif
.Sender
.toString().c_str());
375 // find the channel to callback the creator
376 TChannels::iterator
it(cmi
->_Channels
.begin()), last(cmi
->_Channels
.end());
377 for (; it
!= last
; ++it
)
379 CChatChannelImplementation
*cc
= it
->second
;
381 if (cc
->_ChannelID
== pif
.ChanID
)
383 nldebug("Forwarding player %s input into channel '%s'",
384 eid
.toString().c_str(),
385 cc
->_ChannelName
.c_str());
388 if (cc
->_ChatCallback
!= NULL
)
390 cc
->_ChatCallback
->receiveMessage(pif
.Sender
, pif
.Content
);
398 //-----------------------------------------------------------------------------
399 // methods CChatManager
400 //-----------------------------------------------------------------------------
402 CChatManager
* CChatManager::getInstance()
404 return CChatManagerImplementation::getInstance();
407 //-----------------------------------------------------------------------------
408 // methods CChatChannelImplementation
409 //-----------------------------------------------------------------------------
411 CChatChannelImplementation::CChatChannelImplementation(const CSString
&channelName
)
412 :_ChannelOpen(false),
413 _ChannelName(channelName
)
417 void CChatChannelImplementation::openChannel(const NLMISC::CSString
& channelTitle
, uint32 historySize
, bool noBroadcast
, bool forwardInput
, bool autoInsertPlayer
)
422 // create the channel id
423 uint32 number
= CChatManagerImplementation::getInstance()->generateChannelNumber();
424 _ChannelID
= CEntityId(RYZOMID::dynChatGroup
, uint64(number
));
425 _ChannelTitle
= channelTitle
;
426 _HistorySize
= historySize
;
427 _NoBroadcast
= noBroadcast
;
428 _ForwardInput
= forwardInput
;
429 _AutoInsertPlayer
= autoInsertPlayer
;
431 CMessage
msgout("DYN_CHAT:ADD_SERVICE_CHAN");
432 msgout
.serial(_ChannelID
);
433 msgout
.serial(_ChannelTitle
);
434 msgout
.serial(_NoBroadcast
);
435 msgout
.serial(_ForwardInput
);
437 sendMessageViaMirror( "EGS", msgout
);
439 if (_HistorySize
!= 0)
441 CMessage
msgout("DYN_CHAT:SET_CHAN_HISTORY");
442 msgout
.serial(_ChannelID
);
443 msgout
.serial(_HistorySize
);
445 sendMessageViaMirror("EGS", msgout
);
450 // put all existing client into this new channel
451 CMirroredDataSet
*ds
= CGusMirror::getInstance()->getDataSet("fe_temp");
453 TEntityIdToEntityIndexMap::const_iterator
it(ds
->entityBegin()), last(ds
->entityEnd());
454 for (; it
!= last
; ++it
)
456 if (it
->first
.getType() == RYZOMID::player
)
458 TDataSetRow clientID
= ds
->getDataSetRow(it
->first
);
460 if (_AutoInsertPlayer
461 || (_ChatCallback
&& _ChatCallback
->isClientAllowedInChatChannel(clientID
, this)))
464 // // We have a player here, open a session
466 // TChanID chan = _ChannelID;
467 // bool writeRight = true;
469 // CMessage msgout("DYN_CHAT:ADD_SESSION");
470 // msgout.serial(chan);
471 // msgout.serial(clientID);
472 // msgout.serial(writeRight);
474 // sendMessageViaMirror( "EGS", msgout);
476 // TClientValidation cv;
477 // cv.ClientId = clientID;
478 // cv.EntryDate = CTickEventHandler::getGameCycle();
480 // _ClientToValidate.push_back(cv);
486 void CChatChannelImplementation::closeChannel()
491 CMessage
msgout("DYN_CHAT:REMOVE_SERVICE_CHAN");
492 msgout
.serial(_ChannelID
);
494 sendMessageViaMirror( "EGS", msgout
);
496 _ChannelOpen
= false;
499 void CChatChannelImplementation::addClient(GUS::TClientId clientId
)
501 TChanID chan
= _ChannelID
;
502 bool writeRight
= true;
504 CMessage
msgout("DYN_CHAT:ADD_SESSION");
506 msgout
.serial(clientId
);
507 msgout
.serial(writeRight
);
509 sendMessageViaMirror( "EGS", msgout
);
511 TClientValidation cv
;
512 cv
.ClientId
= clientId
;
513 cv
.EntryDate
= CTickEventHandler::getGameCycle();
515 _ClientToValidate
.push_back(cv
);
518 void CChatChannelImplementation::removeClient(GUS::TClientId clientId
)
520 TChanID chan
= _ChannelID
;
522 CMessage
msgout("DYN_CHAT:REMOVE_SESSION");
524 msgout
.serial(clientId
);
526 sendMessageViaMirror( "EGS", msgout
);
529 void CChatChannelImplementation::broadcastMessage(const ucstring
& speakerName
, const ucstring
& txt
)
531 nldebug("Channel %s : broadcasting \"'%s' says '%s'\"",
532 _ChannelTitle
.c_str(),
533 speakerName
.toString().c_str(),
534 txt
.toString().c_str());
536 CMessage
msgout("DYN_CHAT:SERVICE_CHAT");
537 msgout
.serial(_ChannelID
);
538 msgout
.serial(const_cast<ucstring
&>(speakerName
));
539 msgout
.serial(const_cast<ucstring
&>(txt
));
541 sendMessageViaMirror( "IOS", msgout
);
544 void CChatChannelImplementation::broadcastMessage(const std::string
& speakerNameUtf8
, const std::string
& txtUtf8
)
546 ucstring speakerName
, txt
;
547 speakerName
.fromUtf8(speakerNameUtf8
);
548 txt
.fromUtf8(txtUtf8
);
550 broadcastMessage(speakerName
, txt
);
553 void CChatChannelImplementation::sendMessage(GUS::TClientId clientId
, const ucstring
& speakerName
,const ucstring
& txt
)
555 CEntityId eid
= CGusMirror::getInstance()->getDataSet("fe_temp")->getEntityId(clientId
);
556 nldebug("Channel %s : sending \"'%s' says '%s'\" to client %s",
557 _ChannelTitle
.c_str(),
558 speakerName
.toString().c_str(),
559 txt
.toString().c_str(),
560 eid
.toString().c_str());
562 CMessage
msgout("DYN_CHAT:SERVICE_TELL");
563 msgout
.serial(_ChannelID
);
564 msgout
.serial(const_cast<ucstring
&>(speakerName
));
565 msgout
.serial(clientId
);
566 msgout
.serial(const_cast<ucstring
&>(txt
));
568 sendMessageViaMirror( "IOS", msgout
);
571 void CChatChannelImplementation::sendMessage(GUS::TClientId clientId
, const std::string
& speakerNameUtf8
, const std::string
& txtUtf8
)
573 ucstring speakerName
, txt
;
574 speakerName
.fromUtf8(speakerNameUtf8
);
575 txt
.fromUtf8(txtUtf8
);
577 sendMessage(clientId
, speakerName
, txt
);
580 void CChatChannelImplementation::setChatCallback(IChatCallback
* callback
)
582 _ChatCallback
= callback
;
585 const NLMISC::CSString
& CChatChannelImplementation::getChannelName() const
590 const NLMISC::CSString
& CChatChannelImplementation::getChannelTitle() const
592 return _ChannelTitle
;
595 void CChatChannelImplementation::setChannelTitle(const NLMISC::CSString
& title
)
597 _ChannelTitle
= title
;
598 CIOSMsgSetPhrase(_ChannelName
,_ChannelTitle
).send();
602 //-----------------------------------------------------------------------------
603 // CChat instantiator
604 //-----------------------------------------------------------------------------
606 class CChatManagerImplementationInstantiator
609 CChatManagerImplementationInstantiator()
611 CChatManagerImplementation::getInstance();
614 static CChatManagerImplementationInstantiator ChatManagerInstantiator
;
617 //-----------------------------------------------------------------------------
618 // NLMISC_COMMAND commands
619 //-----------------------------------------------------------------------------
621 NLMISC_COMMAND(chatCreateChannel
, "create a new chat channel", "<channelName> <channelTitle>")
623 if (args
.size() != 2)
626 CChatManagerImplementation
*cmi
= CChatManagerImplementation::getInstance();
628 TChatChannelPtr channel
= cmi
->createChatChannel(args
[0]);
632 channel
->openChannel(args
[1], 0, true, true, true);
636 nlwarning("Failed to create channel '%s'", args
[1].c_str());
642 NLMISC_COMMAND(chatRemoveChannel
, "remove an existing chat channel", "<channelName>")
644 if (args
.size() != 1)
647 CChatManagerImplementation
*cmi
= CChatManagerImplementation::getInstance();
649 TChatChannelPtr channel
= cmi
->getChatChannel(args
[0]);
653 nlwarning ("The channel '%s' is unknow", args
[0].c_str());
657 channel
->closeChannel();
658 cmi
->removeChannel(channel
);
664 NLMISC_COMMAND(chatSendMessage
, "send a chat message in a channel", "<channelName> <senderName> <text>")
669 CChatManagerImplementation
*cmi
= CChatManagerImplementation::getInstance();
671 TChatChannelPtr channel
= cmi
->getChatChannel(args
[0]);
674 nlwarning ("The channel '%s' is unknow", args
[0].c_str());
679 for (uint i
=2; i
<args
.size(); ++i
)
680 text
+= args
[i
] + " ";
681 channel
->broadcastMessage(args
[1], text
);
687 NLMISC_COMMAND(chatDisplayChannel
, "list all the chat channel and their states", "")
692 CChatManagerImplementation
*cmi
= CChatManagerImplementation::getInstance();
694 log
.displayNL("Listing %u chat channel :", cmi
->_Channels
.size());
695 CChatManagerImplementation::TChannels::iterator
it(cmi
->_Channels
.begin()), last(cmi
->_Channels
.end());
696 for (;it
!= last
; ++it
)
698 CChatChannelImplementation
*cmi
= it
->second
;
700 log
.displayNL(" Name '%s', Title '%s', ChanId %s, State '%s'",
701 cmi
->_ChannelName
.c_str(),
702 cmi
->_ChannelTitle
.c_str(),
703 cmi
->_ChannelID
.toString().c_str(),
704 cmi
->_ChannelOpen
? "OPEN" : "CLOSED");
710 NLMISC_COMMAND(chatPlayerInput
, "simulate a player input in a channel", "<player_eid> <channelName> <message>+")
715 CEntityId
eid(args
[0]);
716 TDataSetRow index
= CGusMirror::getInstance()->getDataSet("fe_temp")->getDataSetRow(eid
);
717 if (index
== INVALID_DATASET_ROW
)
719 log
.displayNL("Unknow player '%s'", eid
.toString().c_str());
723 // Retreive the channel
724 TChatChannelPtr channel
= CChatManager::getInstance()->getChatChannel(args
[1]);
727 log
.displayNL("Unknown channel '%s'", args
[1].c_str());
730 CChatChannelImplementation
*cmi
= static_cast<CChatChannelImplementation
*>(static_cast<CChatChannel
*>(channel
));
732 // concatenate the message
734 for (uint i
=2; i
<args
.size(); ++i
)
737 if (i
< args
.size() -1)
741 TPlayerInputForward pif
;
742 pif
.ChanID
= cmi
->_ChannelID
;
746 CMessage
msg("DYN_CHAT:FORWARD");
750 CChatManagerImplementation::getInstance()->cbDynChatForward(msg
, IService::getInstance()->getServiceAliasName(), IService::getInstance()->getServiceId());
755 //-----------------------------------------------------------------------------
756 // methods CChatBroadcastDisplayer
757 //-----------------------------------------------------------------------------
759 CChatBroadcastDisplayer::CChatBroadcastDisplayer(CChatChannel
* channel
): IDisplayer("gusChatBroadcast")
764 void CChatBroadcastDisplayer::doDisplay(const NLMISC::CLog::TDisplayInfo
& args
,const char *message
)
766 _Channel
->broadcastMessage("*",message
);
770 //-----------------------------------------------------------------------------
771 // methods CChatDisplayer
772 //-----------------------------------------------------------------------------
774 CChatDisplayer::CChatDisplayer(CChatChannel
* channel
,TClientId clientId
): IDisplayer("gusChat")
780 void CChatDisplayer::doDisplay(const NLMISC::CLog::TDisplayInfo
& args
,const char *message
)
782 _Channel
->sendMessage(_ClientId
,"*",message
);
785 //-----------------------------------------------------------------------------