void AbiCollab::addCollaborator(BuddyPtr pCollaborator) { UT_DEBUGMSG(("AbiCollab::addCollaborator()\n")); UT_return_if_fail(pCollaborator) // check if this buddy is in the access control list if we are hosting // this session if (isLocallyControlled()) { AccountHandler* pAccount = pCollaborator->getHandler(); UT_return_if_fail(pAccount); if (!pAccount->hasAccess(m_vAcl, pCollaborator)) { UT_ASSERT(UT_NOT_IMPLEMENTED); return; } } // check for duplicates (as long as we assume a collaborator can only be part of a collaboration session once) std::map<BuddyPtr, std::string>::iterator it = m_vCollaborators.find(pCollaborator); if (it != m_vCollaborators.end()) { UT_DEBUGMSG(("Attempting to add buddy '%s' twice to a collaboration session!", pCollaborator->getDescription().utf8_str())); UT_ASSERT_HARMLESS(UT_SHOULD_NOT_HAPPEN); return; } m_vCollaborators[pCollaborator] = ""; // will fill the remote document UUID later once we receive a packet from this buddy }
void AbiCollabSessionManager::disjoinSession(const UT_UTF8String& sSessionId) { UT_DEBUGMSG(("AbiCollabSessionManager::disjoinSession(%s)\n", sSessionId.utf8_str())); AbiCollab* pSession = getSessionFromSessionId(sSessionId); UT_return_if_fail(pSession); const std::map<BuddyPtr, std::string> vCollaborators = pSession->getCollaborators(); if (!isLocallyControlled(pSession->getDocument())) { // we are joined to a session, so there should only be one collaborator: // the person sharing the document UT_return_if_fail(vCollaborators.size() == 1); std::map<BuddyPtr, std::string>::const_iterator cit = vCollaborators.begin(); BuddyPtr pCollaborator = (*cit).first; destroySession(pSession); DisjoinSessionEvent event(sSessionId); event.addRecipient(pCollaborator); signal(event); } else { UT_ASSERT(UT_NOT_REACHED); return; } }
void AbiCollabSessionManager::disconnectSession(AbiCollab* pSession) { UT_return_if_fail(pSession); UT_DEBUGMSG(("AbiCollabSessionManager::disconnectSession() - pSession: %s\n", pSession->getSessionId().utf8_str())); if (isLocallyControlled(pSession->getDocument())) { /* Before we close this session, try to see of we can hand over session ownership to someone else. NOTE: we don't do any fancy determination yet who to hand over the session to; we just hand it over to the first buddy in the list. */ if (_canInitiateSessionTakeover(pSession)) { UT_DEBUGMSG(("Can initiate session takeover\n")); if (pSession->getCollaborators().size() > 0) { std::map<BuddyPtr, std::string>::const_iterator cit = pSession->getCollaborators().begin(); pSession->initiateSessionTakeover((*cit).first); } } closeSession(pSession, false); } else { disjoinSession(pSession->getSessionId()); } // TODO: mark the session as disconnected/closed, for additional safety }
/*! * Send this packet. Note, the specified packet does still belong to the calling class. * So if we want to store it (for masking), we HAVE to clone it first */ void AbiCollab::push(SessionPacket* pPacket) { UT_DEBUGMSG(("AbiCollab::push()\n")); UT_return_if_fail(pPacket); if (m_bIsReverting) { UT_DEBUGMSG(("This packet was generated by a local revert triggerd in the import; dropping on the floor!\n")); return; } if (m_bExportMasked) { m_vecMaskedPackets.push_back(static_cast<SessionPacket*>(pPacket->clone())); // TODO: make this a shared ptr, so we don't need to clone the packet return; } if (!isLocallyControlled() && m_eTakeoveState != STS_NONE) { // TODO: revert ack packets should still go to old master // (or be dropped on the floor, as he probably is not even around anymore) UT_DEBUGMSG(("We're in the middle of a session takeover; holding on to the packet until the new master is ready")); m_vOutgoingQueue.push_back(static_cast<SessionPacket*>(pPacket->clone())); // TODO: make this a shared ptr, so we don't need to clone the packet return; } // record if (m_pRecorder) m_pRecorder->storeOutgoing( const_cast<const SessionPacket*>( pPacket ) ); // TODO: this could go in the session manager UT_DEBUGMSG(("Pusing packet to %d collaborators\n", m_vCollaborators.size())); for (std::map<BuddyPtr, std::string>::iterator it = m_vCollaborators.begin(); it != m_vCollaborators.end(); it++) { BuddyPtr pCollaborator = (*it).first; UT_continue_if_fail(pCollaborator); UT_DEBUGMSG(("Pushing packet to collaborator with descriptor: %s\n", pCollaborator->getDescriptor(true).utf8_str())); AccountHandler* pHandler = pCollaborator->getHandler(); UT_continue_if_fail(pHandler); // overwrite remote revision for this collaborator _fillRemoteRev(pPacket, pCollaborator); // send! bool res = pHandler->send(pPacket, pCollaborator); if (!res) { UT_DEBUGMSG(("Error sending a packet!\n")); } } }
void AbiCollab::_checkRevokeAccess(BuddyPtr pCollaborator) { UT_DEBUGMSG(("AbiCollab::_checkRevokeAccess()\n")); UT_return_if_fail(pCollaborator); UT_return_if_fail(isLocallyControlled()); UT_return_if_fail(m_pAclAccount); // remove this buddy from the access control list if his access rights // are not persistent if (!pCollaborator->getHandler()->hasPersistentAccessControl()) { for (std::vector<std::string>::iterator it = m_vAcl.begin(); it != m_vAcl.end(); it++) { if (pCollaborator->getDescriptor(false) == (*it)) { UT_DEBUGMSG(("Dropping %s from the ACL\n", (*it).c_str())); m_vAcl.erase(it); break; } } } }
void AbiCollab::startRecording( SessionRecorderInterface* pRecorder ) { UT_return_if_fail(pRecorder); const UT_GenericVector<ChangeAdjust *>* pExpAdjusts = m_Export.getAdjusts(); UT_return_if_fail(pExpAdjusts); // FIXME: fill this properly UT_sint32 iAuthorId = -1; UT_ASSERT_HARMLESS(UT_NOT_IMPLEMENTED); // create initial document packet to recorder // so the recorder knows the initial state // serialize entire document into string JoinSessionRequestResponseEvent jsre(getSessionId(), iAuthorId); if (AbiCollabSessionManager::serializeDocument(m_pDoc, jsre.m_sZABW, false /* no base64 */) == UT_OK) { // set more document properties if (!isLocallyControlled()) { UT_ASSERT_HARMLESS(pExpAdjusts->getItemCount() > 0); jsre.m_iRev = (pExpAdjusts->getItemCount() > 0 ? pExpAdjusts->getNthItem(pExpAdjusts->getItemCount()-1)->getLocalRev() : 0); } else jsre.m_iRev = m_pDoc->getCRNumber(); jsre.m_sDocumentId = m_pDoc->getDocUUIDString(); if (m_pDoc->getFilename()) jsre.m_sDocumentName = UT_go_basename_from_uri(m_pDoc->getFilename()); // store pointer m_pRecorder = pRecorder; m_pRecorder->storeOutgoing( &jsre ); } else { UT_ASSERT(UT_SHOULD_NOT_HAPPEN); } }
/** * input_ is available only on server or on the local client, not for * remote tanks. */ void Tank::frameMove(float dt) { PROFILE(Tank::frameMove); setSleeping(false); Controllable::frameMove(dt); // Weapon firing code is handled on client for local player only // to have immediate feedback. Remote players receive weapon // feedback by received state. bool firing = false; if (getLocation() == CL_SERVER_SIDE || (getLocation() == CL_CLIENT_SIDE && isLocallyControlled())) { firing |= weapon_system_[0]->handleInput(input_.fire1_); firing |= weapon_system_[1]->handleInput(input_.fire2_); firing |= weapon_system_[2]->handleInput(input_.fire3_); firing |= weapon_system_[3]->handleInput(input_.fire4_); } if (getLocation() != CL_REPLAY_SIM) { for(unsigned w=0; w < NUM_WEAPON_SLOTS; w++) { weapon_system_[w]->frameMove(dt); } } if (is_locally_controlled_ && getLocation() == CL_CLIENT_SIDE) { frameMoveTurret(dt, true); } frameMoveTurret(dt, false); if (is_locally_controlled_ || getLocation() == CL_SERVER_SIDE) { // remote tanks don't have accurate wheel info, and no extra // dampening needed anyway handleExtraDampening(); // Don't calc tire physics for uncontrolled objects on client. frameMoveTires(dt); } if (location_ == CL_SERVER_SIDE) { /// heal logic if (!firing && params_.get<bool>("tank.heal_skill") && getGlobalLinearVel().length() < s_params.get<float>("server.logic.tank_heal_velocity_threshold") && getOwner() != UNASSIGNED_SYSTEM_ADDRESS) { startHealing(); } else { stopHealing(); } // object is positioned by visual on client side. Avoid redundant // positioning positionCarriedObject(); if (firing) { setInvincible(false); } } }
void AbiCollab::import(SessionPacket* pPacket, BuddyPtr collaborator) { UT_DEBUGMSG(("AbiCollab::import()\n")); UT_return_if_fail(pPacket); if (m_bDoingMouseDrag) { // we block incoming packets while dragging the mouse; this prevents // scary race conditions from occuring, like importing a 'delete image' packet // when you are just dragging said image around UT_DEBUGMSG(("We are currently dragging something around; deferring packet import until after the release!\n")); m_vIncomingQueue.push_back( std::make_pair(static_cast<SessionPacket*>(pPacket->clone()), collaborator)); return; } // record the incoming packet if (m_pRecorder) m_pRecorder->storeIncoming(pPacket, collaborator); // execute an alternative packet handling path when this session is being // taken over by another collaborator if (AbstractSessionTakeoverPacket::isInstanceOf(*pPacket)) { AbstractSessionTakeoverPacket* astp = static_cast<AbstractSessionTakeoverPacket*>(pPacket); bool res = _handleSessionTakeover(astp, collaborator); if (!res) { // TODO: implement/handle an offending collaborator UT_ASSERT_HARMLESS(UT_SHOULD_NOT_HAPPEN); } return; } /* Session packets are only allowed to come in from a collaborator when: 1. no session takeover is in progress, or 2a. if this session is a slave: always 2b. if this session is a master: until the collaborator has responded to a SessionTakeoverRequest from us with a SessionTakeoverAck packet */ // TODO: implement/handle an offending collaborator UT_return_if_fail( (m_eTakeoveState == STS_NONE) || (!isLocallyControlled()) || (isLocallyControlled() && m_eTakeoveState == STS_SENT_TAKEOVER_REQUEST && !_hasAckedSessionTakeover(collaborator)) ); // import the packet; note that it might be denied due to collisions maskExport(); if (AbstractChangeRecordSessionPacket::isInstanceOf(*pPacket)) m_pActivePacket = static_cast<const AbstractChangeRecordSessionPacket*>(pPacket); m_vCollaborators[collaborator] = pPacket->getDocUUID().utf8_str(); // FIXME: this is redunant after we set this the first time m_Import.import(*pPacket, collaborator); m_pActivePacket = NULL; const std::vector<SessionPacket*>& maskedPackets = unmaskExport(); if (isLocallyControlled() && maskedPackets.size() > 0) { UT_DEBUGMSG(("Forwarding message (%u packets) from %s\n", maskedPackets.size(), collaborator->getDescription().utf8_str())); // It seems we are in the center of a collaboration session. // It's our duty to reroute the packets to the other collaborators for (std::map<BuddyPtr, std::string>::iterator it = m_vCollaborators.begin(); it != m_vCollaborators.end(); it++) { // send all masked packets during import to everyone, except to the // person who initialy sent us the packet BuddyPtr pBuddy = (*it).first; UT_continue_if_fail(pBuddy); if (pBuddy != collaborator) { UT_DEBUGMSG(("Forwarding message from %s to %s\n", collaborator->getDescription().utf8_str(), pBuddy->getDescription().utf8_str())); for (std::vector<SessionPacket*>::const_iterator cit = maskedPackets.begin(); cit != maskedPackets.end(); cit++) { SessionPacket* maskedPacket = (*cit); push(maskedPacket, pBuddy); } } } } }
bool AbiCollabSessionManager::processPacket(AccountHandler& /*handler*/, Packet* packet, BuddyPtr buddy) { UT_DEBUGMSG(("AbiCollabSessionManager::processPacket()\n")); UT_return_val_if_fail(packet, false); UT_return_val_if_fail(buddy, false); // check if this is a simple import-meh-now-packet PClassType pct = packet->getClassType(); if (pct >= _PCT_FirstSessionPacket && pct <= _PCT_LastSessionPacket) { // lookup session SessionPacket* dsp = static_cast<SessionPacket*>( packet ); const UT_UTF8String& sessionId = dsp->getSessionId(); AbiCollab* pAbiCollab = getSessionFromSessionId(sessionId); if (!pAbiCollab) { UT_DEBUGMSG(("Unknown session id: '%s'", sessionId.utf8_str())); UT_return_val_if_fail(pAbiCollab, true); } // handle packet! pAbiCollab->import(dsp, buddy); return true; } // handle packet switch (pct) { case PCT_StartSessionEvent: { StartSessionEvent event; event.setBroadcast(true); signal(event, buddy); return true; } case PCT_JoinSessionEvent: { JoinSessionEvent* jse = static_cast<JoinSessionEvent*>(packet); const UT_UTF8String& joinedSessionId = jse->getSessionId(); // someone who joined this session disconnected, remove him from the collaboration session AbiCollab* pSession = getSessionFromSessionId(joinedSessionId); if (pSession) { if (isLocallyControlled( pSession->getDocument() )) { // we should already know this buddy, as we sent should have already added this // buddy when responding to his JoinSessionRequest // TODO: check this } // signal all JoinSessionEvent event(joinedSessionId); signal( event, buddy ); } else { // we don't know this session, don't forward the packet UT_ASSERT_HARMLESS(UT_NOT_REACHED); } return true; } case PCT_DisjoinSessionEvent: { DisjoinSessionEvent* dse = static_cast<DisjoinSessionEvent*>(packet); const UT_UTF8String& disjoinedSessionId = dse->getSessionId(); // someone who joined this session disconnected, remove him from the collaboration session AbiCollab* pSession = getSessionFromSessionId(disjoinedSessionId); if (pSession) { pSession->removeCollaborator(buddy); // signal all DisjoinSessionEvent event(disjoinedSessionId); signal(event, buddy); } else { // we don't know this session, don't forward the packet UT_ASSERT_HARMLESS(UT_NOT_REACHED); } return true; } case PCT_CloseSessionEvent: { CloseSessionEvent* cse = static_cast<CloseSessionEvent*>(packet); const UT_UTF8String& destroyedSessionId = cse->getSessionId(); buddy->destroyDocHandle( destroyedSessionId ); // handle the event outselves AbiCollab* pSession = getSessionFromSessionId(destroyedSessionId); if (pSession) { if (!isLocallyControlled(pSession->getDocument())) { std::string docName = pSession->getDocument()->getFilename(); if (docName == "") docName = "Untitled"; // TODO: fetch the title from the frame somehow (which frame?) - MARCM // the server hosting this session is gone, so let's disconnect as well if (!destroySession(pSession)) { UT_ASSERT_HARMLESS(UT_SHOULD_NOT_HAPPEN); } // signal all CloseSessionEvent event( destroyedSessionId ); signal( event, buddy ); // inform the user of the disconnect XAP_Frame *pFrame = XAP_App::getApp()->getLastFocussedFrame(); UT_return_val_if_fail(pFrame, true); UT_UTF8String msg; // TODO: make this localizable UT_UTF8String_sprintf(msg, "Document %s is not being shared anymore by buddy %s. You are disconnected from the collaboration session.", docName.c_str(), buddy->getDescription().utf8_str()); pFrame->showMessageBox(msg.utf8_str(), XAP_Dialog_MessageBox::b_O, XAP_Dialog_MessageBox::a_OK); } else { // someone who is not controlling this session sends out messages he closed it! // we will not forward this packet UT_ASSERT_HARMLESS(UT_NOT_REACHED); } } else { UT_DEBUGMSG(("Ignoring a CloseSession event for unknown session (%s)\n", destroyedSessionId.utf8_str())); } return true; } case PCT_AccountAddBuddyRequestEvent: { // look at this packet; I have a feeling we need to deprecate it - MARCM UT_ASSERT_HARMLESS(UT_NOT_IMPLEMENTED); return true; } default: break; } return false; }