/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
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, 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
#include "plMsgForwarder.h"
#include "hsResMgr.h"
#include "../pnMessage/plMessage.h"
#include "../pnKeyedObject/plKey.h"
#include "../pnNetCommon/plNetApp.h"
#include "../pnNetCommon/plSynchedObject.h"
#include "../pnMessage/plSelfDestructMsg.h"
#include "../pnMessage/plMessageWithCallbacks.h"
class plForwardCallback
{
public:
hsTArray fOrigReceivers;
int fNumCallbacks;
hsBool fNetPropogate;
};
plMsgForwarder::plMsgForwarder()
{
}
plMsgForwarder::~plMsgForwarder()
{
CallbackMap::iterator i = fCallbacks.begin();
for (; i != fCallbacks.end(); i++)
delete (*i).second;
}
void plMsgForwarder::Read(hsStream* s, hsResMgr* mgr)
{
hsKeyedObject::Read(s, mgr);
int numKeys = s->ReadSwap32();
fForwardKeys.Reset();
fForwardKeys.Expand(numKeys);
fForwardKeys.SetCount(numKeys);
for (int i = 0; i < numKeys; i++)
{
plKey key = mgr->ReadKey(s);
fForwardKeys[i] = key;
}
}
void plMsgForwarder::Write(hsStream* s, hsResMgr* mgr)
{
hsKeyedObject::Write(s, mgr);
int numKeys = fForwardKeys.Count();
s->WriteSwap32(numKeys);
for (int i = 0; i < numKeys; i++)
mgr->WriteKey(s, fForwardKeys[i]);
}
hsBool plMsgForwarder::MsgReceive(plMessage* msg)
{
// Self destruct messages are for us only
plSelfDestructMsg *selfMsg = plSelfDestructMsg::ConvertNoRef(msg);
if (selfMsg)
return hsKeyedObject::MsgReceive(msg);
// If this is a callback message, it needs to be handled differently
if (IForwardCallbackMsg(msg))
return true;
// All other messages are forwarded
IForwardMsg(msg);
return true;
}
hsBool plMsgForwarder::IForwardCallbackMsg(plMessage *msg)
{
// Only process as a callback message if it is one, AND it has callbacks
plMessageWithCallbacks *callbackMsg = plMessageWithCallbacks::ConvertNoRef(msg);
if (callbackMsg && callbackMsg->GetNumCallbacks() > 0)
{
for (int i = 0; i < callbackMsg->GetNumCallbacks(); i++)
{
plEventCallbackMsg *event = callbackMsg->GetEventCallback(i);
hsAssert(event, "Message forwarder only supports event callback messages");
if (event)
{
plForwardCallback *fc = TRACKED_NEW plForwardCallback;
fc->fNumCallbacks = fForwardKeys.Count();
// Turn off net propagate the callbacks to us will all be local. Only the
// callback we send will go over the net
fc->fNetPropogate = (event->HasBCastFlag(plMessage::kNetPropagate) != 0);
event->SetBCastFlag(plMessage::kNetPropagate, false);
for (int j = 0; j < event->GetNumReceivers(); j++)
fc->fOrigReceivers.Append((plKey)event->GetReceiver(j));
event->ClearReceivers();
event->AddReceiver(GetKey());
fCallbacks[event] = fc;
#if 0
hsStatusMessageF("Adding CBMsg, eventSender=%s, eventRemoteMsg=%d\n",
event->GetSender() ? event->GetSender()->GetName() : "nil", fc->fNetPropogate);
#endif
}
}
#if 0
hsStatusMessageF("Fwding CBMsg, sender=%s, remoteMsg=%d",
msg->GetSender() ? msg->GetSender()->GetName() : "nil", msg->HasBCastFlag(plMessage::kNetNonLocal));
#endif
IForwardMsg(callbackMsg);
return true;
}
// Callback from one of our forward keys. Don't send the final callback to the original
// requester until all forward keys have reported in.
plEventCallbackMsg *eventMsg = plEventCallbackMsg::ConvertNoRef(msg);
if (eventMsg)
{
CallbackMap::const_iterator it = fCallbacks.find(eventMsg);
if (it != fCallbacks.end())
{
plForwardCallback *fc = it->second;
if (--fc->fNumCallbacks == 0)
{
hsStatusMessageF("plEventCallbackMsg received, erasing, sender=%s, remoteMsg=%d\n",
msg->GetSender() ? msg->GetSender()->GetName() : "nil", msg->HasBCastFlag(plMessage::kNetNonLocal));
fCallbacks.erase(eventMsg);
plUoid uoid = GetKey()->GetUoid();
hsBool locallyOwned = (plNetClientApp::GetInstance()->IsLocallyOwned(uoid) != plSynchedObject::kNo);
// If the callback was originally net propagated, and we own this forwarder, net propagate the callback
if (fc->fNetPropogate && locallyOwned)
eventMsg->SetBCastFlag(plMessage::kNetPropagate);
eventMsg->ClearReceivers();
eventMsg->AddReceivers(fc->fOrigReceivers);
eventMsg->SetSender(GetKey());
hsRefCnt_SafeRef(eventMsg);
eventMsg->Send();
delete fc;
}
}
else
{
hsStatusMessageF("! Unknown plEventCallbackMsg received, sender=%s, remoteMsg=%d\n",
msg->GetSender() ? msg->GetSender()->GetName() : "nil", msg->HasBCastFlag(plMessage::kNetNonLocal));
hsAssert(0, "Unknown plEventCallbackMsg received");
}
return true;
}
return false;
}
void plMsgForwarder::IForwardMsg(plMessage *msg)
{
// Back up the message's original receivers
hsTArray oldKeys;
for (int i = 0; i < msg->GetNumReceivers(); i++)
oldKeys.Append((plKey)msg->GetReceiver(i));
// Set to our receivers and send
hsRefCnt_SafeRef(msg);
msg->ClearReceivers();
msg->AddReceivers(fForwardKeys);
msg->Send();
// Reset back to the original receivers. This is so we don't screw up objects
// who reuse their messages
msg->ClearReceivers();
msg->AddReceivers(oldKeys);
}
void plMsgForwarder::AddForwardKey(plKey key)
{
if (fForwardKeys.Find(key) == fForwardKeys.kMissingIndex)
fForwardKeys.Append(key);
}