SHARE
TWEET

CrossTalk

a guest Feb 4th, 2014 67 Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. /*
  2.  * Obviously, the core of this needs to have a certain amount of similarities to Mumble's Link plugin,
  3.  * @ https://sourceforge.net/p/mumble/code/ci/master/tree/plugins/link/
  4.  * which is under what appears to be a 3-clause BSD licence also operating under the alias of modified BSD license
  5.  *
  6.  * ToDo: Is this considered a modification, a derivation now?
  7.  * Do I need to put his copyright above this, and/or does this need to be BSD too?
  8.  * It's not like reading a struct is the invention of the wheel or sth.?!?
  9.  */
  10. #include "mod_positionalaudio.h"
  11.  
  12. #include "public_rare_definitions.h"
  13. #include "public_errors.h"
  14.  
  15. #include "ts3_functions.h"
  16. #include "../plugin.h"
  17. #include "../plugin_qt.h"
  18.  
  19. #include "../talkers.h"
  20. #include "../ts_helpers_qt.h"
  21.  
  22. #include "ts_serversinfo.h"
  23.  
  24. #include <db.h>
  25.  
  26. #ifndef M_PI
  27. #define M_PI    3.14159265358979323846f
  28. #endif
  29.  
  30. #ifndef INCHTOM
  31. #define INCHTOM(b) (b*39.3701)
  32. #endif
  33.  
  34. //bool operator==(const TS3_VECTOR &vec, const float *arr)
  35. //{
  36. //    return ((vec.x == arr[0]) && (vec.y == arr[1]) && (vec.z == arr[2]));
  37. //}
  38.  
  39. struct LinkedMem {
  40.     quint32 uiVersion;              //win:UINT32;posix:uint32_t
  41.     ulong   dwcount;                //win:DWORD;posix:uint32_t  - ToDo: this is rather irritating, isn't it?
  42.     float       fAvatarPosition[3];
  43.     float       fAvatarFront[3];
  44.     float       fAvatarTop[3];
  45.     wchar_t     name[256];
  46.     float       fCameraPosition[3];
  47.     float       fCameraFront[3];
  48.     float       fCameraTop[3];
  49.     wchar_t     identity[256];
  50.     quint32 context_len;            //win:UINT32;posix:uint32_t
  51.     unsigned char context[256];
  52.     wchar_t description[2048];
  53. };
  54.  
  55. static LinkedMem *lm = NULL;
  56.  
  57. PositionalAudio::PositionalAudio(QObject *parent) :
  58.     m_tryTimerId(0),
  59.     m_fetchTimerId(0),
  60.     m_fetchTimerTimeoutMsec(5000),
  61.     m_sendCounter(0),
  62.     m_silentSendCounter(2),
  63.     m_Context_Dirty(false),
  64.     m_isDirty_IdentityUncleaned(false),
  65.     m_Avatar_Dirty(false),
  66.     m_lastCount(0),
  67.     m_isUseCamera(true),
  68.     m_isUseAttenuation(false),
  69.     m_distanceMin(0),
  70.     m_distanceMax(0),
  71.     m_rollOff(0.0f),
  72.     m_rollOffMax(0.0f),
  73.     m_rollOff_Lin(1.0f),
  74.     m_rollOffMax_Lin(1.0f),
  75.     m_IsSendAllOverride(true)
  76. {
  77.     this->setParent(parent);
  78.     this->setObjectName("PositionalAudio");
  79.     m_isPrintEnabled = true;
  80.     universe = new TsVrUniverse(this);
  81.     meObj = new TsVrObjSelf(this);
  82.     NULL_VECTOR.x = 0.0f;
  83.     NULL_VECTOR.y = 0.0f;
  84.     NULL_VECTOR.z = 0.0f;
  85. }
  86.  
  87. // Properties
  88. QString PositionalAudio::getMyVr() const
  89. {
  90.     return meObj->getVr();
  91. }
  92.  
  93. QString PositionalAudio::getMyIdentity() const
  94. {
  95.     return meObj->getIdentity();
  96. }
  97.  
  98. bool PositionalAudio::isUseCamera() const
  99. {
  100.     return m_isUseCamera;
  101. }
  102.  
  103. bool PositionalAudio::isUseAttenuation() const
  104. {
  105.     return m_isUseAttenuation;
  106. }
  107.  
  108. int PositionalAudio::getDistanceMin() const
  109. {
  110.     return m_distanceMin;
  111. }
  112.  
  113. int PositionalAudio::getDistanceMax() const
  114. {
  115.     return m_distanceMax;
  116. }
  117.  
  118. float PositionalAudio::getRollOff() const
  119. {
  120.     return m_rollOff;
  121. }
  122.  
  123. float PositionalAudio::getRollOffMax() const
  124. {
  125.     return m_rollOffMax;
  126. }
  127.  
  128. void PositionalAudio::setUseCamera(bool val)
  129. {
  130.     if (m_isUseCamera == val)
  131.         return;
  132.     m_isUseCamera = val;
  133.     emit useCameraChanged(m_isUseCamera);
  134. }
  135.  
  136. void PositionalAudio::setUseAttenuation(bool val)
  137. {
  138.     if (m_isUseAttenuation == val)
  139.         return;
  140.     m_isUseAttenuation = val;
  141.     emit useAttenuationChanged(m_isUseAttenuation);
  142. }
  143.  
  144. void PositionalAudio::setDistanceMin(int val)
  145. {
  146.     if (m_distanceMin == val)
  147.         return;
  148.     m_distanceMin = val;
  149.     emit distanceMinChanged(m_distanceMin);
  150. }
  151.  
  152. void PositionalAudio::setDistanceMax(int val)
  153. {
  154.     if (m_distanceMax == val)
  155.         return;
  156.     m_distanceMax = val;
  157.     emit distanceMaxChanged(m_distanceMax);
  158. }
  159.  
  160. void PositionalAudio::setRollOff(float val)
  161. {
  162.     if (m_rollOff == val)
  163.         return;
  164.     m_rollOff = val;
  165.     m_rollOff_Lin = db2lin_alt2(m_rollOff);
  166.     emit rollOffChanged(m_rollOff);
  167. }
  168.  
  169. void PositionalAudio::setRollOffMax(float val)
  170. {
  171.     if (m_rollOffMax == val)
  172.         return;
  173.     m_rollOffMax = val;
  174.     m_rollOffMax_Lin = db2lin_alt2(m_rollOffMax);
  175.     emit rollOffMaxChanged(m_rollOffMax);
  176. }
  177.  
  178. void PositionalAudio::AddServerSetting(QString serverUniqueId, QString serverName)
  179. {
  180.     if (m_ServerSettings.contains(serverUniqueId))
  181.     {
  182.         if (m_ServerSettings.value(serverUniqueId).serverName != serverName)
  183.             m_ServerSettings[serverUniqueId].serverName = serverName;
  184.  
  185.         return;
  186.     }
  187.  
  188.     PositionalAudio_ServerSettings setting;
  189.     setting.serverName = serverName;
  190.     setting.serverUniqueId = serverUniqueId;
  191.     setting.enabled = false;
  192.     setting.isBlocked = true;
  193.     setting.sendInterval = 1.0f;
  194.     m_ServerSettings.insert(serverUniqueId,setting);
  195. }
  196.  
  197. void PositionalAudio::RemoveServerSetting(QString serverUniqueId, QString serverName)
  198. {
  199.     if (serverUniqueId == QString::null)
  200.     {
  201.         if (serverName == QString::null)
  202.         {
  203.             Error("(RemoveServerSetting) Called with no valid arguments");
  204.             return;
  205.         }
  206.         QMapIterator<QString,PositionalAudio_ServerSettings> i(m_ServerSettings);
  207.         unsigned int count = 0;
  208.         while (i.hasNext())
  209.         {
  210.             i.next();
  211.             PositionalAudio_ServerSettings setting = i.value();
  212.             if (setting.serverName == serverName)
  213.             {
  214.                 count++;
  215.                 serverUniqueId = setting.serverUniqueId;
  216.             }
  217.         }
  218.         if (count > 1)
  219.         {
  220.             Error("(RemoveServerSetting) Multiple remove candidates found. Aborting.");
  221.             return;
  222.         }
  223.         else if (count == 0)
  224.         {
  225.             Error("(RemoveServerSetting) No matching server setting found.");
  226.             return;
  227.         }
  228.     }
  229.  
  230.     m_ServerSettings.remove(serverUniqueId);
  231.     Log(QString("(RemoveServerSetting) %1").arg(serverName),LogLevel_INFO);
  232. }
  233.  
  234. void PositionalAudio::setServerSettingEnabled(QString serverUniqueId, bool val)
  235. {
  236.     if (!m_ServerSettings.contains(serverUniqueId))
  237.         return;
  238.     if (m_ServerSettings.value(serverUniqueId).enabled == val)
  239.         return;
  240.  
  241.     m_ServerSettings[serverUniqueId].enabled = val;
  242. }
  243.  
  244. void PositionalAudio::setServerSettingBlocked(QString serverUniqueId, bool val)
  245. {
  246.     if (!m_ServerSettings.contains(serverUniqueId))
  247.         return;
  248.     if (m_ServerSettings.value(serverUniqueId).isBlocked == val)
  249.         return;
  250.  
  251.     m_ServerSettings[serverUniqueId].isBlocked = val;
  252. //    Print(QString("Block Status Changed: %1 %2").arg(m_ServerSettings.value(serverUniqueId).serverName).arg((m_ServerSettings.value(serverUniqueId).isBlocked)?"blocked":"unblocked"));
  253. }
  254.  
  255. void PositionalAudio::setServerSettingSendInterval(QString serverUniqueId, float val)
  256. {
  257.     if (!m_ServerSettings.contains(serverUniqueId))
  258.         return;
  259.     if (m_ServerSettings.value(serverUniqueId).sendInterval == val)
  260.         return;
  261.  
  262.     m_ServerSettings[serverUniqueId].sendInterval = val;
  263. }
  264.  
  265. void PositionalAudio::setServerSettingSendIntervalSilentInc(QString serverUniqueId, float val)
  266. {
  267.     if (!m_ServerSettings.contains(serverUniqueId))
  268.         return;
  269.     if (m_ServerSettings.value(serverUniqueId).sendIntervalSilentInc == val)
  270.         return;
  271.  
  272.     m_ServerSettings[serverUniqueId].sendIntervalSilentInc = val;
  273. }
  274.  
  275. void PositionalAudio::onMyVrChanged(TsVrObj *obj, QString val)
  276. {
  277.     Q_UNUSED(obj);
  278.     emit myVrChanged(val);
  279. }
  280.  
  281. void PositionalAudio::onMyIdentityChanged(TsVrObj *obj, QString val)
  282. {
  283.     Q_UNUSED(obj);
  284.     emit myIdentityChanged(val);
  285. }
  286.  
  287. void PositionalAudio::onUniverseRemoved(QString clientUID)
  288. {
  289.     QString out_stri;
  290.     QTextStream out(&out_stri);
  291.     out << "{";
  292.     out << "\"uid\":\"" << clientUID << "\",";
  293.     out << "\"me\":false}";
  294.     PluginQt::instance()->LocalServerSend(out_stri);
  295. }
  296.  
  297. QMap<QString, PositionalAudio_ServerSettings> PositionalAudio::getServerSettings() const
  298. {
  299.     return m_ServerSettings;
  300. }
  301.  
  302. /*bool PositionalAudio::RegisterCustomEnvironmentSupport(QObject *p)
  303. {
  304.     CustomEnvironmentSupportInterface *iCustomEnvironmentSupport = qobject_cast<CustomEnvironmentSupportInterface *>(p);
  305.     if (!iCustomEnvironmentSupport)
  306.         return false;
  307.  
  308.     m_CustomEnvironmentSupportMap.insert(p->objectName(),p);
  309.     return true;
  310. }*/
  311.  
  312. // Other
  313. void PositionalAudio::onClientMoveEvent(uint64 serverConnectionHandlerID, anyID clientID, uint64 oldChannelID, uint64 newChannelID, int visibility, anyID myID)
  314. {
  315.     Q_UNUSED(newChannelID);
  316.  
  317.     if (clientID==myID) // && newChannelID != 0 should be filtered out on plugin level already
  318.     {
  319.         QString args = GetSendString(true);
  320.         if (!args.isEmpty())
  321.             Send(serverConnectionHandlerID,args,PluginCommandTarget_CURRENT_CHANNEL,NULL,NULL);
  322.     }
  323.     else
  324.     {
  325.         bool isRemove = (visibility==LEAVE_VISIBILITY);
  326.         if (!isRemove)
  327.         {
  328.             uint64 channelID;
  329.             unsigned int error;
  330.             if((error = ts3Functions.getChannelOfClient(serverConnectionHandlerID,myID,&channelID)) != ERROR_ok)
  331.                 Error("(onClientMoveEvent)",serverConnectionHandlerID,error);
  332.             else
  333.                 isRemove = (oldChannelID==channelID);   // leave without losing visibility
  334.         }
  335.         if (isRemove)
  336.         {
  337.             universe->Remove(serverConnectionHandlerID,clientID);
  338.             if (m_PlayersInMyContext.contains(serverConnectionHandlerID,clientID))
  339.             {
  340.                 m_PlayersInMyContext.remove(serverConnectionHandlerID,clientID);
  341.                 ts3Functions.channelset3DAttributes(serverConnectionHandlerID, clientID, &NULL_VECTOR);
  342.             }
  343.         }
  344.     }
  345. }
  346.  
  347. void PositionalAudio::onCustom3dRolloffCalculationClientEvent(uint64 serverConnectionHandlerID, anyID clientID, float distance, float *volume)
  348. {
  349.     if ((!isRunning()) || (meObj->getVr().isEmpty()))
  350.         return;
  351.  
  352.     if (!m_isUseAttenuation)
  353.         *volume = 1.0f;
  354.     else
  355.     {
  356.         if (!m_PlayersInMyContext.contains(serverConnectionHandlerID,clientID))
  357.             *volume = 1.0f;
  358.         else
  359.         {
  360.             if ((m_distanceMax > 0) && (distance >= m_distanceMax))
  361.                 *volume = 0.0f;
  362.             else if (distance <= m_distanceMin)
  363.                 *volume = 1.0f;
  364.             else
  365.             {
  366.                 distance = distance - m_distanceMin;
  367.                 float rollOff = distance * m_rollOff_Lin;
  368.                 if (rollOff < m_rollOffMax_Lin)
  369.                     rollOff = m_rollOffMax_Lin;
  370.  
  371.                 *volume = rollOff;
  372.             }
  373.         }
  374.     }
  375. }
  376.  
  377. bool PositionalAudio::onInfoDataChanged(uint64 serverConnectionHandlerID, uint64 id, PluginItemType type, uint64 mine, QTextStream &data)
  378. {
  379.     if (!isRunning())
  380.         return false;
  381.  
  382.     bool isDirty = false;
  383.     if (type == PLUGIN_CLIENT)
  384.     {
  385.         if (id == mine)
  386.         {
  387.             QString game = meObj->getVr();
  388.             if (!game.isEmpty())
  389.             {
  390.                 data << this->objectName() << ": ";
  391.                 isDirty = true;
  392.  
  393.                 data << "is playing " << game;
  394.  
  395.                 QString id = meObj->getIdentity();
  396.                 if (!id.isEmpty())
  397.                     data << " as " << id;
  398.             }
  399.         }
  400.         else
  401.         {
  402.             if (universe->Contains(serverConnectionHandlerID,(anyID)id))
  403.             {
  404.                 TsVrObj* obj = universe->Get(serverConnectionHandlerID,(anyID)id);
  405.                 if (!obj->getVr().isEmpty())
  406.                 {
  407.                     data << this->objectName() << ": ";
  408.                     isDirty = true;
  409.  
  410.                     data << "is playing " << obj->getVr();
  411.                     if (!obj->getIdentity().isEmpty())
  412.                     {
  413.                         data << " as " << obj->getIdentity();
  414.                         if (m_PlayersInMyContext.contains(serverConnectionHandlerID,(anyID)id))
  415.                         {
  416.                             data << " in my context";
  417.                         }
  418.                         else
  419.                             data << " outside of my context";
  420. //                        data << ((m_PlayersInMyContext.contains(serverConnectionHandlerID,(anyID)id))?" in my context":" outside of my context");
  421.                     }
  422.                 }
  423.             }
  424.         }
  425.     }
  426.     return isDirty;
  427. }
  428.  
  429. /*int PositionalAudio::onServerErrorEvent(uint64 serverConnectionHandlerID, const char *errorMessage, unsigned int error, const char *returnCode, const char *extraMessage)
  430. {
  431.     Q_UNUSED(errorMessage);
  432.     Q_UNUSED(returnCode);
  433.     Q_UNUSED(extraMessage);
  434.  
  435.     if (isRunning() && (error==ERROR_client_is_flooding))    // check if im sending there once this is implemented
  436.     {        
  437.         QString sUId = TSServersInfo::instance()->GetServerInfo(serverConnectionHandlerID)->getUniqueId();
  438.         emit serverBlock(sUId);
  439.     }
  440.  
  441.     return 0;
  442. }*/
  443.  
  444. void PositionalAudio::onRunningStateChanged(bool value)
  445. {
  446.     m_isDirty_IdentityUncleaned = false;    // not value, we want it to be off no matter what
  447.     m_Context_Dirty = false;
  448.     m_Avatar_Dirty = false;
  449.  
  450.     TSInfoData::instance()->Register(this,value,1);
  451.  
  452.     if (value)
  453.     {
  454. //        if (m_SendReturnCode.isEmpty())
  455. //        {
  456. //            m_SendReturnCodeC = new char[RETURNCODE_BUFSIZE];
  457. //            ts3Functions.createReturnCode(pluginID,m_SendReturnCodeC,RETURNCODE_BUFSIZE);
  458. //            m_SendReturnCode = QString::fromLatin1(m_SendReturnCodeC);
  459. //            Print("Got returnCode: " + m_SendReturnCode);
  460. //        }
  461.         connect(Talkers::instance(),SIGNAL(ConnectStatusChanged(uint64,int,uint)),this,SLOT(onConnectStatusChanged(uint64,int,uint)),Qt::UniqueConnection);
  462.         connect(universe,SIGNAL(removed(QString)),this,SLOT(onUniverseRemoved(QString)),Qt::UniqueConnection);
  463.         connect(meObj,SIGNAL(vrChanged(TsVrObj*,QString)),this,SLOT(onMyVrChanged(TsVrObj*,QString)),Qt::UniqueConnection);
  464.         connect(meObj,SIGNAL(identityChanged(TsVrObj*,QString)),this,SLOT(onMyIdentityChanged(TsVrObj*,QString)),Qt::UniqueConnection);
  465.  
  466. //        Print(QString("DWORD: %1, quint32: %2, ulong: %3").arg(sizeof(DWORD)*8).arg(sizeof(quint32)*8).arg(sizeof(ulong)*8));
  467. //        bool bCreated = false;
  468.  
  469.         m_sharedMemory = new QSharedMemory(this);
  470.         m_sharedMemory->setNativeKey("MumbleLink");
  471.         if (!m_sharedMemory->create(sizeof(LinkedMem),QSharedMemory::ReadWrite))
  472.         {
  473.             if (m_sharedMemory->error() == QSharedMemory::AlreadyExists)
  474.             {
  475.                 if(!m_sharedMemory->attach(QSharedMemory::ReadWrite))
  476.                 {
  477.                     Error(QString("Could not attach to shared memory: %1").arg(m_sharedMemory->errorString()));
  478.                     return;
  479.                 }
  480.             }
  481.             else
  482.             {
  483.                 Error(QString("Could not create shared memory: %1").arg(m_sharedMemory->errorString()));
  484.                 return;
  485.             }
  486.         }
  487.         else
  488.             memset(m_sharedMemory->data(), 0, m_sharedMemory->size());
  489.  
  490.         lm = static_cast<LinkedMem *>(m_sharedMemory->data());
  491.         if (lm == NULL)
  492.         {
  493.             m_sharedMemory->detach();
  494.             Error("Could not cast shared memory to struct.");
  495.             return;
  496.         }
  497.         m_tryTimerId = this->startTimer(1000);
  498.     }
  499.     else
  500.     {
  501.         disconnect(Talkers::instance(),SIGNAL(ConnectStatusChanged(uint64,int,uint)),this,SLOT(onConnectStatusChanged(uint64,int,uint)));
  502.         disconnect(universe,SIGNAL(removed(QString)),this,SLOT(onUniverseRemoved(QString)));
  503.         disconnect(meObj,SIGNAL(vrChanged(TsVrObj*,QString)),this,SLOT(onMyVrChanged(TsVrObj*,QString)));
  504.         disconnect(meObj,SIGNAL(identityChanged(TsVrObj*,QString)),this,SLOT(onMyIdentityChanged(TsVrObj*,QString)));
  505.         unlock();
  506.         m_sharedMemory->detach();
  507.         lm = NULL;
  508.     }
  509.     Log(QString("enabled: %1").arg((value)?"true":"false"));
  510. }
  511.  
  512. void PositionalAudio::timerEvent(QTimerEvent *event)
  513. {
  514.     if (event->timerId() == m_fetchTimerId)
  515.     {
  516.         if (fetch())
  517.         {
  518.             if (m_fetchTimerElapsed != 0) // bool isFetchDirt =
  519.                 return;
  520.  
  521.             Update3DListenerAttributes();
  522.  
  523.             // send the data
  524.             m_sendCounter++;
  525.             Send();
  526.             if (m_isDirty_IdentityUncleaned)
  527.                 TSInfoData::instance()->RequestSelfUpdate();
  528.         }
  529.         else
  530.             unlock();
  531.     }
  532.     else if (event->timerId() == m_tryTimerId) // "trylock" in Mumble plugin API style
  533.     {
  534.         m_sharedMemory->lock();
  535.         if ((lm->uiVersion == 1) || (lm->uiVersion == 2))
  536.         {
  537.             if (lm->dwcount != m_lastCount)
  538.             {
  539.                 if (lm->description[0])
  540.                 {
  541.                     QString vr_desc = QString::fromWCharArray(lm->description,2048);
  542.                     int nullPos = vr_desc.indexOf(QChar::Null,0);
  543.                     if (nullPos == -1)
  544.                         Error("(UpdateMyGame) game description Null Terminator not found.");
  545.                     else if (nullPos != 256)
  546.                         vr_desc.truncate(nullPos);
  547.  
  548.                     meObj->setVrDescription(vr_desc);
  549.                 }
  550.                 else
  551.                     meObj->setVrDescription(QString::null);
  552.  
  553.                 if (lm->name[0])
  554.                 {
  555.                     QString myVr = QString::fromWCharArray(lm->name,256);
  556.                     int nullPos = myVr.indexOf(QChar::Null,0);
  557.                     if (nullPos == -1)
  558.                         Error("(UpdateMyGame) game Null Terminator not found.");
  559.                     else if (nullPos != 256)
  560.                         myVr.truncate(nullPos);
  561.  
  562.                     meObj->setVr(myVr);
  563.  
  564.                     if (!myVr.isEmpty())
  565.                     {
  566.                         // Print("Found " + meObj->getVr());
  567.                         if (m_tryTimerId != 0)
  568.                         {
  569.                             this->killTimer(m_tryTimerId);
  570.                             m_tryTimerId = 0;
  571.                         }
  572.                         m_lastCount = lm->dwcount;
  573.                         m_fetchTimerElapsed = 0;
  574.                         m_fetchTimerId = this->startTimer(FETCH_TIMER_INTERVAL);
  575.                     }
  576.                 }
  577.                 else
  578.                     meObj->setVr(QString::null);
  579.             }
  580.         }
  581.         m_sharedMemory->unlock();
  582.     }
  583. }
  584.  
  585. void PositionalAudio::unlock()
  586. {
  587.     m_Context.clear();
  588.     m_IdentityUncleaned.clear();
  589.  
  590.     if (lm)
  591.     {
  592.         m_sharedMemory->lock();
  593.         lm->dwcount = m_lastCount = 0;
  594.         lm->uiVersion = 0;
  595.         lm->name[0] = 0;
  596.         lm->identity[0] = 0;
  597.         lm->description[0] = 0;
  598.         m_sharedMemory->unlock();
  599.     }
  600.     if (m_fetchTimerId != 0)
  601.     {
  602.         killTimer(m_fetchTimerId);
  603.         m_fetchTimerId = 0;
  604.     }
  605.     if (isRunning() && m_tryTimerId == 0)
  606.         m_tryTimerId = this->startTimer(1000);
  607.  
  608.     meObj->resetAvatar();
  609.     meObj->resetCamera();
  610.  
  611.     m_PlayersInMyContext.clear();
  612.     Update3DListenerAttributes();
  613.  
  614.     meObj->setIdentityRaw(QString::null);
  615.     meObj->setContext(QString::null);
  616.     meObj->setVr(QString::null);
  617.     meObj->setVrDescription(QString::null);
  618.  
  619.     Send(QString::null,PluginCommandTarget_CURRENT_CHANNEL);
  620.     Log("Unlocked.");
  621. }
  622.  
  623. bool PositionalAudio::fetch()
  624. {
  625.     m_sharedMemory->lock();
  626.     if ((lm->uiVersion != 1) && (lm->uiVersion != 2))
  627.         return false;   // -> unlock
  628.  
  629.     if (lm->dwcount == m_lastCount)
  630.     {
  631.         m_fetchTimerElapsed = m_fetchTimerElapsed + FETCH_TIMER_INTERVAL;
  632. //        Print(QString("fetch: elapsed: %1 timeout at: %2").arg(m_fetchTimerElapsed).arg(m_fetchTimerTimeoutMsec));
  633.         m_sharedMemory->unlock();
  634.         return !(m_fetchTimerElapsed > m_fetchTimerTimeoutMsec);    // -> unlock on false
  635.     }
  636.     m_fetchTimerElapsed = 0;
  637.     m_lastCount = lm->dwcount;
  638.  
  639. //    m_Avatar_Dirty = (m_Avatar_Dirty || !((meObj->getAvatarPosition() == lm->fAvatarPosition) && (meObj->getAvatarFront() == lm->fAvatarFront) && (meObj->getAvatarTop() == lm->fAvatarTop)));
  640.  
  641.     m_Avatar_Dirty = (meObj->setAvatar(lm->fAvatarPosition,lm->fAvatarFront,lm->fAvatarTop) || m_Avatar_Dirty);
  642.  
  643.     if (lm->uiVersion == 2)
  644.     {
  645.         //*** Camera
  646.         meObj->setCamera(lm->fCameraPosition,lm->fCameraFront,lm->fCameraTop);
  647.  
  648.         if (lm->context_len > 255)
  649.             lm->context_len = 255;
  650.  
  651.         //*** Context
  652.         QByteArray newContext = QByteArray(reinterpret_cast<const char *>(lm->context),lm->context_len);
  653.         m_Context_Dirty = (m_Context != newContext);
  654.         m_Context = newContext;
  655.         if (m_Context_Dirty)
  656.             meObj->setContext(m_Context.toHex().data());    //m_ContextHex = m_Context.toHex().data();
  657.  
  658.         //*** Identity
  659.         QString newIdentity = QString::fromWCharArray(lm->identity,256);
  660.         m_isDirty_IdentityUncleaned = (m_IdentityUncleaned != newIdentity);
  661.         if (m_isDirty_IdentityUncleaned)
  662.         {
  663.             m_IdentityUncleaned = newIdentity;
  664.             int nullPos = newIdentity.indexOf(QChar::Null,0);
  665.             if (nullPos == -1)
  666.                 Error("identity Null Terminator not found.");
  667.             else if (nullPos != 256)
  668.                 newIdentity.truncate(nullPos);
  669.  
  670.             meObj->setIdentityRaw(newIdentity);
  671.         }
  672.     }
  673.     else if (lm->uiVersion == 1)    //legacy
  674.     {
  675.         meObj->setCamera(lm->fAvatarPosition,lm->fAvatarFront,lm->fAvatarTop);
  676.  
  677.         m_Context.clear();
  678.         //m_ContextHex.clear();
  679.         m_Context_Dirty = false;
  680.         meObj->setContext(QString::null);
  681.         m_IdentityUncleaned.clear();
  682.         m_isDirty_IdentityUncleaned = false;
  683.         meObj->setIdentityRaw(QString::null);
  684.     }
  685.  
  686.     m_sharedMemory->unlock();
  687.     return true;
  688. }
  689.  
  690. bool PositionalAudio::onPluginCommand(uint64 serverConnectionHandlerID, anyID clientID, bool isMe, QString cmd, QTextStream &args)
  691. {
  692.     if (cmd != "3D")
  693.         return false;
  694.  
  695.     if (isMe)
  696.     {
  697.         if (args.atEnd())
  698.         {
  699.             Print("I left vr.");
  700.             return true;
  701.         }
  702.  
  703.         TS3_VECTOR myAvatarPosition;
  704.         TS3_VECTOR myAvatarFront;
  705.         TS3_VECTOR myAvatarTop;
  706.         args >> myAvatarPosition >> myAvatarFront >> myAvatarTop;
  707.  
  708.         if (!args.atEnd())
  709.         {
  710.             //name
  711.             //context
  712.             //identity
  713.             QString args_stri = args.readAll().trimmed();
  714.             QStringList list = args_stri.split("[Ct_Delimiter]",QString::KeepEmptyParts,Qt::CaseSensitive);    // keep empty parts?
  715.             QString name = list.at(0);
  716.             if (list.size() > 1)
  717.             {
  718.                 QString rest = list.at(1);
  719.                 QTextStream in(&rest);
  720.  
  721.                 QString context;
  722.                 in >> context;
  723.  
  724.                 QString identity = in.readAll().trimmed();
  725.  
  726.                 Print(QString("Received (me): cId: %1 VR: %2 CO: %3 ID: %4").arg(clientID).arg(name).arg((context == meObj->getContext())?"match":"no match -.-").arg(identity), serverConnectionHandlerID, LogLevel_DEBUG);
  727.             }
  728.             else    //version 1
  729.             {
  730.                 Print(QString("Received (me): cId: %1 VR: %2").arg(clientID).arg(name), serverConnectionHandlerID, LogLevel_DEBUG);
  731.             }
  732.         }
  733. //        else
  734. //            Print("Received(me) AV",serverConnectionHandlerID,LogLevel_DEBUG);
  735.  
  736.         return true;
  737.     }
  738.  
  739.     TsVrObjOther* obj = universe->Get(serverConnectionHandlerID,clientID);
  740.     if (!obj)
  741.     {
  742.         obj = universe->Add(serverConnectionHandlerID,clientID);
  743.         m_IsSendAllOverride = true;
  744.     }
  745.  
  746.     if (args.atEnd())   // Player left vr (unlocked)
  747.     {
  748.         Log(QString("%1 left %2 VR.").arg(obj->getIdentity()).arg(obj->getVr()));
  749.         universe->Remove(serverConnectionHandlerID,clientID);
  750.         m_PlayersInMyContext.remove(serverConnectionHandlerID,clientID);
  751.         return true;
  752.     }
  753.  
  754.     //args >> obj->avatarPosition >> obj->avatarFront >> obj->avatarTop;
  755.     TS3_VECTOR avatarPosition;
  756.     TS3_VECTOR avatarFront;
  757.     TS3_VECTOR avatarTop;
  758.     args >> avatarPosition >> avatarFront >> avatarTop;
  759.     obj->setAvatar(avatarPosition, avatarFront, avatarTop);
  760.  
  761.     if (!args.atEnd())
  762.     {
  763.         //name
  764.         //context
  765.         //identity
  766.         QString args_stri = args.readAll().trimmed();
  767.         QStringList list = args_stri.split("[Ct_Delimiter]",QString::KeepEmptyParts,Qt::CaseSensitive);    // keep empty parts?
  768.         QString name = list.at(0);
  769.  
  770.         bool isDirtyName = (name != obj->getVr());
  771.         obj->setVr(name);
  772.         bool isDirtyContext = false;
  773.         bool isDirtyId = false;
  774.  
  775.         if (list.size() > 1)
  776.         {
  777.             QString rest = list.at(1);
  778.             QTextStream in(&rest);
  779.  
  780.             QString context;
  781.             in >> context;
  782.             isDirtyContext = (context != obj->getContext());
  783.             obj->setContext(context);
  784.  
  785.             QString identity = in.readAll().trimmed();
  786.             isDirtyId = (identity != obj->getIdentityRaw());
  787.             obj->setIdentityRaw(identity);
  788.  
  789.             Log(QString("Received: VR: %2 CO: %3 ID: %4").arg(name).arg((context == meObj->getContext())?"match":"no match").arg(identity), serverConnectionHandlerID, LogLevel_DEBUG);
  790.  
  791.             if (isDirtyName || isDirtyContext)
  792.             {
  793.                 if (meObj->getVr() == obj->getVr())
  794.                 {
  795.                     if (obj->getContext() == meObj->getContext())
  796.                     {
  797.                         if (!m_PlayersInMyContext.contains(serverConnectionHandlerID,clientID))
  798.                             m_PlayersInMyContext.insert(serverConnectionHandlerID,clientID);
  799.                     }
  800.                     else if (m_PlayersInMyContext.contains(serverConnectionHandlerID,clientID))
  801.                         m_PlayersInMyContext.remove(serverConnectionHandlerID,clientID);
  802.                 }
  803.                 else
  804.                     m_PlayersInMyContext.remove(serverConnectionHandlerID,clientID);
  805.             }
  806.         }
  807.         else    //version 1
  808.         {
  809.             Print(QString("Received: cId: %1 VR: %2").arg(clientID).arg(name), serverConnectionHandlerID, LogLevel_DEBUG);
  810.         }
  811.  
  812.         if (isDirtyName || isDirtyContext || isDirtyId)
  813.             TSInfoData::instance()->RequestUpdate(serverConnectionHandlerID,clientID);
  814.     }
  815.  
  816.     if (m_PlayersInMyContext.contains(serverConnectionHandlerID,clientID))
  817.     {
  818.         TS3_VECTOR vector = obj->getAvatarPosition();
  819.         ts3Functions.channelset3DAttributes(serverConnectionHandlerID,clientID,&vector);
  820.     }
  821.  
  822.     PluginQt::instance()->LocalServerSend(GetSendStringJson(true,false,obj));
  823.     return true;
  824. }
  825.  
  826. QString PositionalAudio::GetSendString(bool isAll)
  827. {
  828.     // Bulk version
  829.     if (meObj->getVr().isEmpty())
  830.         return QString::null;
  831.  
  832.     QString out_stri;
  833.     QTextStream out(&out_stri);
  834.     out << meObj->getAvatarPosition() << meObj->getAvatarFront() << meObj->getAvatarTop();
  835.     if (isAll)
  836.     {
  837. //        out << " " << m_GameName;
  838.         out << " " << meObj->getVr();
  839.  
  840.         if (lm->uiVersion == 2)
  841.         {
  842. //            out << "[Ct_Delimiter]" << (m_ContextHex.isEmpty()?"[Ct_None]":m_ContextHex);
  843.             QString myContext = meObj->getContext();
  844.             out << "[Ct_Delimiter]" << (myContext.isEmpty()?"[Ct_None]":myContext);
  845.             QString my_ident = meObj->getIdentityRaw();
  846.             if (!my_ident.isEmpty())
  847.                 out << " " << my_ident;
  848.         }
  849.     }
  850.     return out_stri;
  851. }
  852.  
  853. QString PositionalAudio::GetSendStringJson(bool isAll, bool isMe, TsVrObj* obj)
  854. {
  855.     if (isMe)
  856.         obj = meObj;
  857.     //else if (isMe.)
  858.  
  859.     QString out_stri;
  860.     QTextStream out(&out_stri);
  861.     out << "{";
  862.     TS3_VECTOR vec = obj->getAvatarPosition();
  863.     out << "\"px\":" << INCHTOM(vec.x) << "," << "\"pz\":" << INCHTOM(vec.z) << ",";// << "\"ap_z\":" << vec.z << ",";
  864. //    Log(QString("%1 %2 %3").arg(vec.x).arg(vec.y).arg(vec.z));
  865.     vec = obj->getAvatarFront();
  866. //    Log(QString("%1 %2 %3").arg(vec.x).arg(vec.y).arg(vec.z));
  867.     int front = atan2(vec.z, vec.x)*180/M_PI;
  868.     if (front <0)
  869.         front += 360;
  870.  
  871.     front *= -1;
  872.     out << "\"pa\":" << front << ",";
  873. //    out << "\"af_x\":" << vec.x << "," << "\"af_y\":" << vec.y << "," << "\"af_z\":" << vec.z << ",";
  874. //    vec = obj->getAvatarTop();
  875. //    out << "\"at_x\":" << vec.x << "," << "\"at_y\":" << vec.y << "," << "\"at_z\":" << vec.z << ",";
  876.  
  877.     if (isMe)
  878.     {
  879.         TsVrObjSelf *iMeObj = qobject_cast<TsVrObjSelf *>(obj);
  880.         if (iMeObj)
  881.         {
  882. //            vec = iMeObj->getCameraPosition();
  883. //            out << "\"cp_x\":" << vec.x << "," << "\"cp_y\":" << vec.y << "," << "\"cp_z\":" << vec.z << ",";
  884. //            vec = iMeObj->getCameraFront();
  885. //            out << "\"cf_x\":" << vec.x << "," << "\"cf_y\":" << vec.y << "," << "\"cf_z\":" << vec.z << ",";
  886. //            vec = iMeObj->getCameraTop();
  887. //            out << "\"ct_x\":" << vec.x << "," << "\"ct_y\":" << vec.y << "," << "\"ct_z\":" << vec.z << ",";
  888.         }
  889.     }
  890.  
  891.     if (isAll)
  892.     {
  893. //        out << "\"vr\":" << obj->getVr() << ",";
  894. //        QString context = obj->getContext();
  895. //        out << "\"co\":" << ((context.isEmpty())?" ":context) << ",";
  896.         QString ident = obj->getIdentityRaw();
  897.         if (ident.isEmpty())
  898.         {
  899.             Log("ident is empty!",LogLevel_INFO);   //fixed with >1.5.0
  900.             return QString::null;
  901.         }
  902.  
  903.         ident.chop(1);
  904.         ident.remove(0,1);
  905. //        if (ident.isEmpty())
  906. //            Error("ident is now empty!");
  907.         //out << "\"id\":" << ident << ",";
  908.         out << ident << ",";
  909.  
  910.         if (!isMe)
  911.         {
  912.             TsVrObjOther *iObj = qobject_cast<TsVrObjOther *>(obj);
  913.             if (iObj)
  914.             {
  915.                 unsigned int error;
  916.                 char name[512];
  917.                 uint64 serverConnectionHandlerID = iObj->getServerConnectionHandlerID();
  918.                 anyID clientID = iObj->getClientID();
  919.                 if((error = ts3Functions.getClientDisplayName(serverConnectionHandlerID, clientID, name, 512)) != ERROR_ok)
  920.                     Error("(GetSendStringJson)",serverConnectionHandlerID,error);
  921.                 else
  922.                     out << "\"vcname\":\"" << name << "\",";
  923.  
  924.                 /*QString uid;
  925.                 if (TSHelpers::GetClientUID(serverConnectionHandlerID,clientID,uid) != ERROR_ok)
  926.                     Error("(GetSendStringJson)",serverConnectionHandlerID,error);
  927.                 else
  928.                     out << "\"uid\":\"" << uid << "\",";*/
  929.                 out << "\"uid\":\"" << iObj->getClientUID() << "\",";
  930.             }
  931.         }
  932.     }
  933.     else
  934.         Error("not isAll!");
  935.  
  936.     out << "\"me\":" << ((isMe)?"true":"false") << "}";
  937.     return out_stri;
  938. }
  939.  
  940. //! Non-throttled DoSend
  941. void PositionalAudio::Send(uint64 serverConnectionHandlerID, QString args, int targetMode, const anyID *targetIDs, const char *returnCode)
  942. {
  943.     unsigned int error;
  944.  
  945.     // needed to be check beforehand
  946. //    int status;
  947. //    if ((error = ts3Functions.getConnectionStatus(serverConnectionHandlerID,&status)) != ERROR_ok)
  948. //    {
  949. //        Error("(Send)",serverConnectionHandlerID,error);
  950. //        return;
  951. //    }
  952. //    if (status != STATUS_CONNECTION_ESTABLISHED)
  953. //        return;
  954.  
  955.     anyID myID;
  956.     if ((error = ts3Functions.getClientID(serverConnectionHandlerID,&myID)) != ERROR_ok)
  957.     {
  958.         Error("(Send)",serverConnectionHandlerID,error);
  959.         return;
  960.     }
  961.  
  962.     QString cmd;
  963.     QTextStream str(&cmd);
  964.     if (args.isEmpty())
  965.         str << myID << " 3D";   // left game (unlocked)
  966.     else
  967.         str << myID << " 3D " << args;
  968.  
  969. //    Print(QString("Sending: %1").arg(cmd),serverConnectionHandlerID,LogLevel_DEBUG);
  970. //    returnCode = m_SendReturnCodeC;
  971.     ts3Functions.sendPluginCommand(serverConnectionHandlerID,pluginID,cmd.toLatin1().constData(),targetMode,targetIDs,returnCode);
  972. }
  973.  
  974. void PositionalAudio::Send(QString args, int targetMode)
  975. {
  976.     if (!m_ServerSettings.contains("default"))
  977.         return;
  978.  
  979.     // Get clients in my context
  980.     uint64* servers;
  981.     if(ts3Functions.getServerConnectionHandlerList(&servers) == ERROR_ok)
  982.     {
  983. //        m_silentSendCounter++;
  984.  
  985.         uint64 myTalkingScHandler = Talkers::instance()->isMeTalking();
  986.  
  987.         uint64* server;
  988.         for(server = servers; *server != (uint64)NULL; ++server)
  989.         {
  990.             TSServerInfo* serverInfo = TSServersInfo::instance()->GetServerInfo(*server);
  991.             if (serverInfo == (TSServerInfo*)NULL)
  992.                 continue;
  993.  
  994. //            unsigned int error;
  995. //            int status;
  996. //            if ((error = ts3Functions.getConnectionStatus(*server,&status)) != ERROR_ok)
  997. //            {
  998. //                Error("(Send)",*server,error);
  999. //                continue;
  1000. //            }
  1001. //            if (status != STATUS_CONNECTION_ESTABLISHED)
  1002. //            {
  1003. //                Error(QString("(Send) %1 not connected").arg(*server));
  1004. //                continue;
  1005. //            }
  1006.  
  1007.             QString sUId = serverInfo->getUniqueId();
  1008.             const PositionalAudio_ServerSettings s_settings = (m_ServerSettings.contains(sUId)?m_ServerSettings.value(sUId):m_ServerSettings.value("default"));
  1009.             if (!s_settings.enabled)
  1010.                 continue;
  1011.  
  1012.             if (targetMode == PluginCommandTarget_CLIENT)
  1013.             {
  1014.                 if ((m_ServerSettings.contains(sUId)) && m_ServerSettings.value(sUId).isBlocked)    //don't fallback on "default", which should never be isBlocked
  1015.                     continue;
  1016.  
  1017.                 int count_max = (s_settings.sendInterval) * SEND_INTERVAL_MODIFIER; // with SEND_THROTTLE_GLOBAL == 5 normalized; todo dynamic
  1018.                 if (myTalkingScHandler != *server)
  1019.                 {
  1020. //                    Print("Adding silent interval increase");
  1021.                     count_max += (s_settings.sendIntervalSilentInc * SEND_INTERVAL_MODIFIER);
  1022.                 }
  1023.  
  1024.                 if (m_SendCounters[*server]++ < count_max)
  1025.                     continue;
  1026.  
  1027. //                Print(QString("count: %1; max count: %2").arg(m_SendCounters[*server]+1).arg(count_max));
  1028.                 m_SendCounters[*server] = 0;
  1029.  
  1030.                 /*QVector<anyID> vec = QVector<anyID>::fromList(m_PlayersInMyContext.values(*server));
  1031.                 if (!vec.isEmpty())
  1032.                 {
  1033.                     // For testing purposes
  1034.                     anyID myID;
  1035.                     unsigned int error;
  1036.                     if ((error = ts3Functions.getClientID(*server,&myID)) == ERROR_ok)
  1037.                         vec.append(myID);
  1038.  
  1039.                     vec.append((anyID)0);
  1040.                     Send(*server, args, targetMode, vec.constData(), NULL);
  1041.                 }*/
  1042.                 Send(*server, args, PluginCommandTarget_CURRENT_CHANNEL, NULL, NULL);
  1043.             }
  1044.             else
  1045.                 Send(*server, args, targetMode, NULL, NULL);
  1046.         }
  1047.         ts3Functions.freeMemory(servers);
  1048.     }
  1049.     m_sendCounter = 0;
  1050. }
  1051.  
  1052. void PositionalAudio::Send()
  1053. {
  1054.     if (m_Context_Dirty || m_isDirty_IdentityUncleaned || m_IsSendAllOverride)
  1055.     {
  1056.         if (m_Context_Dirty)
  1057.             Log(QString("New context: %1").arg(meObj->getContext()));
  1058.  
  1059.         if (m_isDirty_IdentityUncleaned)
  1060.             Log("New identity: " + meObj->getIdentityRaw());
  1061.  
  1062.         m_IsSendAllOverride = false;
  1063.         QString args = GetSendString(true);
  1064.         if (!args.isEmpty())
  1065.         {
  1066.             m_Avatar_Dirty = false;
  1067.             m_Context_Dirty = false;
  1068.             m_isDirty_IdentityUncleaned = false;
  1069.             Send(args,PluginCommandTarget_CURRENT_CHANNEL);
  1070.  
  1071.             args = GetSendStringJson(true,true,NULL);
  1072.             if (!args.isEmpty())
  1073.                 PluginQt::instance()->LocalServerSend(args);
  1074.         }
  1075.  
  1076.  
  1077.     }
  1078.     else if (m_sendCounter > SEND_THROTTLE_GLOBAL)    //was: 50
  1079.     {
  1080.         m_silentSendCounter++;
  1081.         if (m_Avatar_Dirty)
  1082.         {
  1083.             QString args = GetSendString(false);
  1084.             if (!args.isEmpty())
  1085.             {
  1086.                 m_Avatar_Dirty = false;
  1087.                 Send(args,PluginCommandTarget_CLIENT);
  1088.                 args = GetSendStringJson(true,true,NULL);
  1089.                 if (!args.isEmpty())
  1090.                     PluginQt::instance()->LocalServerSend(args);
  1091.             }
  1092.         }
  1093.     }
  1094. }
  1095.  
  1096. void PositionalAudio::Update3DListenerAttributes()
  1097. {
  1098.     uint64* servers;
  1099.     if(ts3Functions.getServerConnectionHandlerList(&servers) == ERROR_ok)
  1100.     {
  1101.         QString myVr = meObj->getVr();
  1102.         QString myContext = meObj->getContext();
  1103.         uint64* server;
  1104.         for(server = servers; *server != (uint64)NULL; ++server)
  1105.         {
  1106.             int status;
  1107.             if (ts3Functions.getConnectionStatus(*server, &status) != ERROR_ok)
  1108.                 continue;
  1109.  
  1110.             if (status != STATUS_CONNECTION_ESTABLISHED)
  1111.                 continue;
  1112.  
  1113.             TS3_VECTOR cameraPos   = m_isUseCamera ? meObj->getCameraPosition() : meObj->getAvatarPosition();
  1114.             TS3_VECTOR cameraFront = m_isUseCamera ? meObj->getCameraFront() : meObj->getAvatarFront();
  1115.             TS3_VECTOR cameraTop   = m_isUseCamera ? meObj->getCameraTop() : meObj->getAvatarTop();
  1116.  
  1117.             ts3Functions.systemset3DListenerAttributes(*server,&cameraPos,&cameraFront,&cameraTop);
  1118.  
  1119.             unsigned int error;
  1120.             // Get My Id on this handler
  1121.             anyID myID;
  1122.             if((error = ts3Functions.getClientID(*server,&myID)) != ERROR_ok)
  1123.                 Error("(Update3DListenerAttributes)");
  1124.             else
  1125.             {
  1126.                 // Get My channel on this handler
  1127.                 uint64 channelID;
  1128.                 if((error=ts3Functions.getChannelOfClient(*server,myID,&channelID)) != ERROR_ok)
  1129.                     Error("(Update3DListenerAttributes)",*server,error);
  1130.                 else
  1131.                 {
  1132.                     // Get Channel Client List
  1133.                     anyID* clients;
  1134.                     if((error = ts3Functions.getChannelClientList(*server, channelID, &clients)) != ERROR_ok)
  1135.                         Error("(Update3DListenerAttributes)", *server, error);
  1136.                     else
  1137.                     {
  1138.                         if (m_Context_Dirty)
  1139.                             m_PlayersInMyContext.clear();
  1140.  
  1141.                         for(int i=0; clients[i]; i++)
  1142.                         {
  1143.                             if (m_Context_Dirty)    // Refill m_PlayersInMyContext
  1144.                             {
  1145.                                 if (universe->Contains(*server,clients[i]))
  1146.                                 {
  1147.                                     TsVrObj* obj = universe->Get(*server,clients[i]);
  1148.                                     if ((myVr == obj->getVr()) && (myContext == obj->getContext()))
  1149.                                         m_PlayersInMyContext.insert(*server,clients[i]);
  1150.                                 }
  1151.                             }
  1152.  
  1153.                             if (!m_PlayersInMyContext.contains(*server,clients[i]))
  1154.                             {
  1155.                                 ts3Functions.channelset3DAttributes(*server,clients[i],&cameraPos);
  1156.                             }
  1157.                         }
  1158.                     }
  1159.                 }
  1160.             }
  1161.         }
  1162.         ts3Functions.freeMemory(servers);
  1163.     }
  1164. }
  1165.  
  1166. void PositionalAudio::onConnectStatusChanged(uint64 serverConnectionHandlerID, int newStatus, unsigned int errorNumber)
  1167. {
  1168.     Q_UNUSED(errorNumber);
  1169.  
  1170.     if (newStatus == STATUS_CONNECTION_ESTABLISHED)
  1171.     {
  1172.         m_SendCounters.insert(serverConnectionHandlerID,0);
  1173.         unsigned int error;
  1174.         // Set system 3d settings
  1175.         if((error = ts3Functions.systemset3DSettings(serverConnectionHandlerID, 1.0f, 0.1f)) != ERROR_ok)
  1176.             Error("(onConnectStatusChanged)",serverConnectionHandlerID,error);
  1177.         else
  1178.             Log("System 3D Settings set.");
  1179.     }
  1180.     else if (newStatus == STATUS_DISCONNECTED)
  1181.     {
  1182.         universe->Remove(serverConnectionHandlerID);
  1183.         m_PlayersInMyContext.remove(serverConnectionHandlerID);
  1184.         m_SendCounters.remove(serverConnectionHandlerID);
  1185.     }
  1186. }
  1187.  
  1188. QTextStream &operator<<(QTextStream &out, const TS3_VECTOR &ts3Vector)
  1189. {
  1190.     out << " " << ts3Vector.x << " " << ts3Vector.y << " " << ts3Vector.z;
  1191.     return out;
  1192. }
  1193.  
  1194. QTextStream &operator>>(QTextStream &in, TS3_VECTOR &ts3Vector)
  1195. {
  1196.     in >> ts3Vector.x >> ts3Vector.y >> ts3Vector.z;
  1197.     return in;
  1198. }
RAW Paste Data
We use cookies for various purposes including analytics. By continuing to use Pastebin, you agree to our use of cookies as described in the Cookies Policy. OK, I Understand
Top