EvEmu  0.8.4
11 September 2021
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
SystemBubble.cpp
Go to the documentation of this file.
1 /*
2  * ------------------------------------------------------------------------------------
3  * LICENSE:
4  * ------------------------------------------------------------------------------------
5  * This file is part of EVEmu: EVE Online Server Emulator
6  * Copyright 2006 - 2021 The EVEmu Team
7  * For the latest information visit https://evemu.dev
8  * ------------------------------------------------------------------------------------
9  * This program is free software; you can redistribute it and/or modify it under
10  * the terms of the GNU Lesser General Public License as published by the Free Software
11  * Foundation; either version 2 of the License, or (at your option) any later
12  * version.
13  *
14  * This program is distributed in the hope that it will be useful, but WITHOUT
15  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16  * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public License along with
19  * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
20  * Place - Suite 330, Boston, MA 02111-1307, USA, or go to
21  * http://www.gnu.org/copyleft/lesser.txt.
22  * ------------------------------------------------------------------------------------
23  * Author: Zhur
24  * Updates: Allan
25  */
26 
27 #include <algorithm>
28 
29 #include "eve-server.h"
30 #include "EVEServerConfig.h"
31 
32 #include "Client.h"
33 #include "EntityList.h"
34 #include "npc/Drone.h"
35 #include "npc/NPC.h"
36 #include "system/BubbleManager.h"
37 #include "system/Container.h"
38 #include "system/DestinyManager.h"
39 #include "system/SystemBubble.h"
40 #include "system/SystemEntity.h"
41 #include "system/SystemManager.h"
43 
44 
45 SystemBubble::SystemBubble(SystemManager* pSystem, const GPoint& center, double radius)
46 : m_system(pSystem),
47 m_center(center),
48 m_radius(radius),
49 m_tcuSE(nullptr),
50 m_sbuSE(nullptr),
51 m_ihubSE(nullptr),
52 m_towerSE(nullptr),
53 m_centerSE(nullptr),
54 m_spawnTimer(0)
55 {
56  m_ice = false;
57  m_belt = false;
58  m_gate = false;
59  m_anomaly = false;
60  m_mission = false;
61  m_spawned = false;
62  m_incursion = false;
63  m_hasBubble = false;
64  m_hasMarkers = false;
65 
66  m_markers.clear();
67  m_players.clear();
68  m_entities.clear();
69  m_dynamicEntities.clear();
70 
71  m_systemID = pSystem->GetID();
72  m_bubbleID = sBubbleMgr.GetBubbleID();
73 
74  _log(DESTINY__BUBBLE_TRACE, "SystemBubble::Constructor - Created new bubble %u(%p) at (%.2f,%.2f,%.2f)[%.1f].",\
76 }
77 
79 {
80  if (m_hasMarkers)
81  for (auto cur : m_markers) {
82  cur.second->Delete(); // delete marker cans here
83  SafeDelete(cur.second);
84  }
85 }
86 
88  if (m_hasMarkers)
89  for (auto cur : m_markers) {
90  cur.second->Delete(); // delete marker cans here
91  SafeDelete(cur.second);
92  }
93 
94  m_ice = false;
95  m_belt = false;
96  m_gate = false;
97  m_anomaly = false;
98  m_mission = false;
99  m_spawned = false;
100  m_incursion = false;
101  m_hasBubble = false;
102  m_hasMarkers = false;
103 
104  m_markers.clear();
105  m_players.clear();
106  m_entities.clear();
107  m_dynamicEntities.clear();
108 }
109 
111 {
112  /* this will need to process:
113  * belt and gate for spawn/respawn
114  * missions for ??
115  * incursions for ??
116  */
117  if (m_belt and (m_system->GetSystemSecurityRating() > 0.90)) // make config option here to spawn rats in secure empire space? nope.
118  return;
119  if (m_spawned) {
121  return;
122  }
123 
124  // this must run a second time for spawn to actually hit. first time only sets main system spawn timer.
125  // may be nuts, but will remain enabled as long as player in bubble and bubble has no rats.
126  if (m_spawnTimer.Enabled()) {
127  if (m_spawnTimer.Check()) {
128  if (!m_players.empty()) {
129  m_system->DoSpawnForBubble(this);
130  } else {
132  }
133  }
134  }
135 }
136 
137 //called every 30s from the bubble manager.
138 //verifies that each entity is still in this bubble.
139 //if any entity is no longer in the bubble, they are removed
140 //from the bubble and stuck into the vector for re-classification.
141 void SystemBubble::ProcessWander(std::vector<SystemEntity*> &wanderers) {
142  DynamicSystemEntity* pDSE(nullptr);
143  std::map<uint32, SystemEntity*>::iterator itr = m_dynamicEntities.begin();
144  while (itr != m_dynamicEntities.end()) {
145  if (itr->second == nullptr) {
146  itr = m_dynamicEntities.erase(itr);
147  continue;
148  }
149  pDSE = itr->second->GetDynamicSE();
150  if (pDSE == nullptr) {
151  itr = m_dynamicEntities.erase(itr);
152  continue;
153  }
154  if ((pDSE->DestinyMgr() == nullptr) or pDSE->DestinyMgr()->IsWarping()) {
155  ++itr;
156  continue;
157  }
158  // is this shit really needed??
159  if (pDSE->SystemMgr()->GetID() != m_systemID) {
160  // this entity is in a different system! this shouldnt happen....
161  // remove this entity, insert into wanderers, and continue
162  wanderers.push_back(pDSE);
163  _log(DESTINY__WARNING, "SystemBubble::ProcessWander() - entity %u is in %u but this is %u.", \
164  pDSE->GetID(), pDSE->SystemMgr()->GetID(), m_systemID);
165  itr = m_dynamicEntities.erase(itr);
166  pDSE = nullptr;
167  continue;
168  }
169  if (!InBubble(pDSE->GetPosition())) {
170  wanderers.push_back(pDSE);
171  //17:38:57 [DestinyWarning] SystemBubble::ProcessWander() - entity 140006173(sys:30002507) not in bubble 1 for systemID 30002510.
172  _log(DESTINY__WARNING, "SystemBubble::ProcessWander() - entity %u(sys:%u) not in bubble %u for systemID %u.", \
173  pDSE->GetID(), pDSE->SystemMgr()->GetID(), m_bubbleID, m_systemID);
174  itr = m_dynamicEntities.erase(itr);
175  pDSE = nullptr;
176  continue;
177  }
178  ++itr;
179  }
180  pDSE = nullptr;
181 
182  if (!m_players.empty() and m_spawned)
184 }
185 
187 {
188  //if they are already in this bubble, do not continue.
189  if (m_entities.find(pSE->GetID()) != m_entities.end()) {
190  _log(DESTINY__BUBBLE_TRACE, "SystemBubble::Add() - Tried to add Static Entity %u to bubble %u, but it is already in here.",\
191  pSE->GetID(), m_bubbleID);
192  return;
193  }
194 
195  pSE->m_bubble = this;
196  // global entities also in SystemMgr's static list. this is used for SystemBubble->IsEmpty() deletion check
197  if (pSE->IsStaticEntity() or pSE->isGlobal()) {
198  _log(DESTINY__BUBBLE_TRACE, "SystemBubble::Add() - Entity %s(%u) is static or global or both.", pSE->GetName(), pSE->GetID() );
199  // all static and global entities (stations, gates, asteroid fields, cyno fields, etc) are put into bubble's staticEntity map
200  m_entities[pSE->GetID()] = pSE;
201  return;
202  }
203 
204  //if they are already in this bubble, do not continue.
205  if (m_dynamicEntities.find(pSE->GetID()) != m_dynamicEntities.end()) {
206  _log(DESTINY__BUBBLE_TRACE, "SystemBubble::Add() - Tried to add Dynamic Entity %u to bubble %u, but it is already in here.",\
207  pSE->GetID(), m_bubbleID);
208  return;
209  }
210 
211  _log(DESTINY__BUBBLE_TRACE, "SystemBubble::Add() - Adding entity %u to bubble %u. Dist to center: %.2f", \
212  pSE->GetID(), m_bubbleID, m_center.distance(pSE->GetPosition()));
213 
214  if (is_log_enabled(DESTINY__BUBBLE_DEBUG)) {
215  GPoint startPoint( pSE->GetPosition() );
216  GVector direction(startPoint, NULL_ORIGIN);
217  double rangeToStar = direction.length();
218  rangeToStar /= ONE_AU_IN_METERS;
219  _log(DESTINY__BUBBLE_DEBUG, "SystemBubble::Add() - Distance to Star %.2f AU. %u/%u Entities in bubble %u",\
220  rangeToStar, m_entities.size(), m_dynamicEntities.size(), m_bubbleID);
221  //if (sConfig.debug.StackTrace)
222  // EvE::traceStack();
223  }
224 
225  if (pSE->HasPilot()) {
226  // Set spawn timer for this bubble, if needed
227  if (m_belt) {
228  // check for roids and load/spawn as needed.
230  if (sConfig.npc.RoamingSpawns)
231  if (!m_spawnTimer.Enabled())
232  SetSpawnTimer(true);
233  }
234  if (m_gate and sConfig.npc.StaticSpawns)
235  if (!m_spawnTimer.Enabled())
236  SetSpawnTimer(false);
237 
238  Client* pClient(pSE->GetPilot());
239  SendAddBalls( pSE );
240  if (!m_players.empty())
241  AddBallExclusive(pSE); // adds new player to all players in bubble, if any
242 
243  m_players[pClient->GetCharacterID()] = pClient; //add to bubble's player list
244  } else {
245  if (!m_players.empty())
246  AddBallExclusive(pSE);
247  if (pSE->IsDroneSE())
248  m_drones[pSE->GetID()] = pSE->GetDroneSE();
249  }
250 
251  // all non-global entities (players, npcs, roids, containers, etc) are put into bubble's dynamicEntity map
252  m_dynamicEntities[pSE->GetID()] = pSE;
253 }
254 
256  //assume that the entity is properly registered for its ID
257  if (pSE->m_bubble == nullptr) {
258  if (sConfig.debug.StackTrace)
259  EvE::traceStack();
260  return;
261  }
262 
263  _log(DESTINY__BUBBLE_TRACE, "SystemBubble::Remove() - Removing entity %u from bubble %u", pSE->GetID(), m_bubbleID);
264 
265  m_entities.erase(pSE->GetID());
266  m_dynamicEntities.erase(pSE->GetID());
267 
268  if (pSE->HasPilot()) {
269  m_players.erase(pSE->GetPilot()->GetCharacterID());
270  RemoveBalls(pSE);
271  }
272 
273  //notify everybody else in the bubble of the removal.
274  if (!m_players.empty())
275  RemoveBall(pSE);
276 
277  if (pSE->IsDroneSE())
278  m_drones.erase(pSE->GetID());
279 
280  if (is_log_enabled(DESTINY__BUBBLE_DEBUG))
281  sLog.Warning("SystemBubble::Remove()", "Removing entity %u from bubble %u", pSE->GetID(), m_bubbleID);
282 
283  pSE->m_bubble = nullptr;
284 }
285 
287  if (pSE->m_bubble == nullptr)
288  return;
289 
290  _log(DESTINY__BUBBLE_TRACE, "SystemBubble::RemoveExclusive() - Removing entity %u from bubble %u", pSE->GetID(), m_bubbleID);
291  RemoveBallExclusive(pSE);
292 }
293 
295 {
296  /* the current spawn in this bubble was killed off, so reset timers accordingly
297  * once the timer hits, it will do all needed checks for players and respawn as needed.
298  * this enables creating a new spawn after previous group was killed off
299  */
300  m_spawned = false;
301  if (m_belt and sConfig.npc.RoamingSpawns)
302  if (!m_spawnTimer.Enabled())
303  SetSpawnTimer(true);
304  if (m_gate and sConfig.npc.StaticSpawns) /* m_gate = false. will fix when gate spawns are finished */
305  if (!m_spawnTimer.Enabled())
306  SetSpawnTimer(false);
307 }
308 
309 void SystemBubble::SetSpawnTimer(bool isBelt/*false*/)
310 {
311  if (m_system->GetSystemSecurityRating() > 0.90)
312  return;
313  if (sConfig.debug.SpawnTest) {
314  m_spawnTimer.Start(5000); /* 5s for testing */
315  } else {
316  // these randoms should be changed to reflect this npc's faction presence in system
317  if (isBelt) {
318  m_spawnTimer.Start(MakeRandomInt(30, sConfig.npc.RoamingTimer) *1000);
319  } else {
320  m_spawnTimer.Start(MakeRandomInt(60, sConfig.npc.StaticTimer) *1000);
321  }
322  }
323 }
324 
326 {
327  m_belt = true;
328  sBubbleMgr.AddSpawnID(m_bubbleID, itemRef->itemID());
329  m_system->GetBeltMgr()->RegisterBelt(itemRef);
330  if (itemRef->typeID() == 17774)
331  m_ice = true;
332 }
333 
335 {
336  m_gate = true;
337  sBubbleMgr.AddSpawnID(m_bubbleID, gateID);
338 }
339 
341  /* updated to send ONLY dynamic entities to the following: -allan 17Apr15
342  * ModuleManager::Activate() --for module activation (with a target)
343  */
344  std::map<uint32, SystemEntity*>::const_iterator itr = m_dynamicEntities.find(entityID);
345  if (itr != m_dynamicEntities.end())
346  return itr->second;
347 
348  return nullptr;
349 }
350 
351 void SystemBubble::GetEntities(std::map<uint32, SystemEntity*> &into) const {
352  /* updated to send non-cloaked dynamic entities to the following: -allan 14Feb15
353  * SystemManager::MakeSetState() --for player entering new system
354  * Command_killallnpcs() --GM command
355  * StructureSE::InitData() --Get TowerSE for pos items
356  */
357  if (m_dynamicEntities.empty())
358  return;
359 
360  for (auto cur : m_dynamicEntities) {
361  if (cur.second->DestinyMgr() != nullptr)
362  if (cur.second->DestinyMgr()->IsCloaked())
363  continue;
364  into.emplace(cur.first, cur.second);
365  }
366 }
367 
368 void SystemBubble::GetAllEntities(std::map< uint32, SystemEntity* >& into) const
369 {
370  if (m_dynamicEntities.empty())
371  return;
372 
373  for (auto cur : m_dynamicEntities)
374  into.emplace(cur.first, cur.second);
375 }
376 
377 
378 void SystemBubble::GetEntityVec(std::vector< SystemEntity* >& into) const
379 {
380  if (m_players.empty())
381  return;
382 
383  for (auto cur : m_dynamicEntities)
384  into.push_back(cur.second);
385 }
386 
387 void SystemBubble::GetPlayers(std::vector<Client*> &into) const {
388  /* updated to send ONLY players to the following: -allan 14Feb15
389  * NPCAIMgr::Process() --for npc targeting
390  * SpawnEntry::Process() --for npc spawning
391  *
392  * this will also send player drones once that system is completed
393  */
394  into.clear();
395  if (m_players.empty())
396  return;
397 
398  for (auto cur : m_players)
399  into.push_back(cur.second);
400 }
401 
403 {
404  // this is used for idle npc's as a orbit target while waiting for something to pewpew
405  if (m_dynamicEntities.empty())
406  return nullptr;
407 
408  for (auto cur : m_dynamicEntities) {
409  if (cur.second->IsWreckSE())
410  return cur.second;
411  if (cur.second->IsObjectEntity())
412  return cur.second;
413  }
414  return nullptr;
415 }
416 
418  uint32 count = 0;
419  for (auto cur : m_dynamicEntities)
420  if (cur.second->IsNPCSE())
421  ++count;
422 
423  return count;
424 }
425 
426 bool SystemBubble::InBubble(const GPoint& pt, bool inWarp/*false*/) const
427 {
428  if (is_log_enabled(DESTINY__BUBBLE_DEBUG)) {
429  float distance = m_center.distance(pt);
430  bool check = false;
431  if (distance < m_radius + 5000) // 5k is the grey area between bubbles
432  check = true;
433 
434  _log(DESTINY__BUBBLE_DEBUG, "SystemBubble::InBubble(%u) - center: %.1f,%.1f,%.1f - distance: %.1f, check: %s", \
435  m_center.x, m_center.y, m_center.z, m_bubbleID, distance, check?"true":"false");
436  return check;
437  }
438 
439  return (m_center.distance(pt) < m_radius);
440 }
441 
442 bool SystemBubble::IsOverlap( const GPoint& pt ) const
443 {
444  if (is_log_enabled(DESTINY__BUBBLE_DEBUG)) {
445  float distance = m_center.distance(pt);
446  bool check = false;
447  if (distance < m_radius * 2 + 10)
448  check = true;
449 
450  _log(DESTINY__BUBBLE_DEBUG, "SystemBubble::IsOverlap(%u) - center: %.1f,%.1f,%.1f - distance: %.1f, check: %s", \
451  m_center.x, m_center.y, m_center.z, m_bubbleID, distance, check?"true":"false");
452  return check;
453  }
454 
455  return (m_center.distance(pt) < (m_radius * 2));
456 }
457 
459  bool found = false;
460  for (auto cur : m_dynamicEntities) {
461  found = false;
462  if (cur.second->isGlobal()) //this should only hit beacons and cynos as global AND not static
463  sLog.Warning( "SystemBubble::PrintEntityList()", "entity %s(%u) is Global.", cur.second->GetName(), cur.second->GetID() );
464  if (cur.second->IsShipSE()) {
465  if (cur.second->HasPilot()) {
466  sLog.Warning( "SystemBubble::PrintEntityList()", "entity %s(%u) is Player Ship.", cur.second->GetName(), cur.second->GetID() ); found = true;
467  } else {
468  sLog.Warning( "SystemBubble::PrintEntityList()", "entity %s(%u) is Empty Player Ship.", cur.second->GetName(), cur.second->GetID() ); found = true;
469  }
470  }
471  if (cur.second->IsNPCSE()) {
472  sLog.Warning( "SystemBubble::PrintEntityList()", "entity %s(%u) is NPC.", cur.second->GetName(), cur.second->GetID() ); found = true;
473  }
474  if (cur.second->IsJumpBridgeSE()) {
475  sLog.Warning( "SystemBubble::PrintEntityList()", "entity %s(%u) is JumpBridge.", cur.second->GetName(), cur.second->GetID() ); found = true;
476  }
477  if (cur.second->IsTCUSE()) {
478  sLog.Warning( "SystemBubble::PrintEntityList()", "entity %s(%u) is TCU.", cur.second->GetName(), cur.second->GetID() ); found = true;
479  }
480  if (cur.second->IsSBUSE()) {
481  sLog.Warning( "SystemBubble::PrintEntityList()", "entity %s(%u) is SBU.", cur.second->GetName(), cur.second->GetID() ); found = true;
482  }
483  if (cur.second->IsIHubSE()) {
484  sLog.Warning( "SystemBubble::PrintEntityList()", "entity %s(%u) is IHub.", cur.second->GetName(), cur.second->GetID() ); found = true;
485  }
486  if (cur.second->IsCOSE()) {
487  sLog.Warning( "SystemBubble::PrintEntityList()", "entity %s(%u) is Customs Office.", cur.second->GetName(), cur.second->GetID() ); found = true;
488  }
489  if (cur.second->IsTowerSE()) {
490  sLog.Warning( "SystemBubble::PrintEntityList()", "entity %s(%u) is Tower.", cur.second->GetName(), cur.second->GetID() ); found = true;
491  }
492  if (cur.second->IsPOSSE() and !found) {
493  sLog.Warning( "SystemBubble::PrintEntityList()", "entity %s(%u) is other POS.", cur.second->GetName(), cur.second->GetID() ); found = true;
494  }
495  if (cur.second->IsContainerSE()) {
496  sLog.Warning( "SystemBubble::PrintEntityList()", "entity %s(%u) is Container.", cur.second->GetName(), cur.second->GetID() ); found = true;
497  }
498  if (cur.second->IsWreckSE()) {
499  sLog.Warning( "SystemBubble::PrintEntityList()", "entity %s(%u) is Wreck.", cur.second->GetName(), cur.second->GetID() ); found = true;
500  }
501  if (cur.second->IsOutpostSE()) {
502  sLog.Warning( "SystemBubble::PrintEntityList()", "entity %s(%u) is Outpost.", cur.second->GetName(), cur.second->GetID() ); found = true;
503  }
504  if (cur.second->IsAsteroidSE()) {
505  sLog.Warning( "SystemBubble::PrintEntityList()", "entity %s(%u) is Asteroid.", cur.second->GetName(), cur.second->GetID() ); found = true;
506  }
507  if (cur.second->IsDeployableSE()) {
508  sLog.Warning( "SystemBubble::PrintEntityList()", "entity %s(%u) is Deployable.", cur.second->GetName(), cur.second->GetID() ); found = true;
509  }
510  if (cur.second->IsStaticEntity() and !found) {
511  sLog.Warning( "SystemBubble::PrintEntityList()", "entity %s(%u) is Static.", cur.second->GetName(), cur.second->GetID() ); found = true;
512  }
513  if (cur.second->IsItemEntity() and !found) {
514  sLog.Warning( "SystemBubble::PrintEntityList()", "entity %s(%u) is Item.", cur.second->GetName(), cur.second->GetID() ); found = true;
515  }
516  if (cur.second->IsObjectEntity() and !found) {
517  sLog.Warning( "SystemBubble::PrintEntityList()", "entity %s(%u) is Object.", cur.second->GetName(), cur.second->GetID() ); found = true;
518  }
519  if (cur.second->IsDynamicEntity() and !found) {
520  sLog.Warning( "SystemBubble::PrintEntityList()", "entity %s(%u) is Dynamic.", cur.second->GetName(), cur.second->GetID() ); found = true;
521  }
522  if (!found)
523  sLog.Warning( "SystemBubble::PrintEntityList()", "entity %s(%u) is None of the Above.", cur.second->GetName(), cur.second->GetID() );
524  }
525 }
526 
528  if (!m_system->IsLoaded())
529  return;
530  if (m_dynamicEntities.empty())
531  return;
532  if (!to_who->HasPilot())
533  return;
534  Client* pClient = to_who->GetPilot();
535  if (pClient == nullptr)
536  return;
537  if (is_log_enabled(DESTINY__BUBBLE_DEBUG))
538  PrintEntityList();
539 
540  Buffer* destinyBuffer = new Buffer();
541 
543  head.packet_type = 1; // 0 = full state 1 = balls
544  head.stamp = sEntityList.GetStamp();
545  destinyBuffer->Append(head);
546 
547  AddBalls addballs;
548  addballs.slims = new PyList();
549 
550  for (auto cur : m_dynamicEntities) {
551  if (cur.second->DestinyMgr() != nullptr)
552  if (cur.second->DestinyMgr()->IsCloaked())
553  continue;
554  if (!cur.second->IsMissileSE() or !cur.second->IsFieldSE())
555  addballs.damageDict[cur.first] = cur.second->MakeDamageState();
556  addballs.slims->AddItem( new PyObject( "foo.SlimItem", cur.second->MakeSlimItem() ) );
557  cur.second->EncodeDestiny( *destinyBuffer );
558  }
559 
560  if (addballs.slims->empty()) {
561  SafeDelete( destinyBuffer );
562  return;
563  }
564 
565  addballs.state = new PyBuffer( &destinyBuffer );
566 
567  _log(DESTINY__MESSAGE, "SystemBubble::SendAddBalls() to %s", pClient->GetName());
568  if (is_log_enabled(DESTINY__BALL_DUMP))
569  addballs.Dump( DESTINY__BALL_DUMP, " " );
570  _log( DESTINY__BALL_DECODE, " Ball Decoded:" );
571  if (is_log_enabled(DESTINY__BALL_DECODE))
572  Destiny::DumpUpdate( DESTINY__BALL_DECODE, &( addballs.state->content() )[0], (uint32)addballs.state->content().size() );
573  PyTuple* t = addballs.Encode();
574  pClient->QueueDestinyUpdate( &t ); //consumed
575 }
576 
578  if (!m_system->IsLoaded())
579  return;
580  if (m_dynamicEntities.empty())
581  return;
582  if (!to_who->HasPilot())
583  return;
584  Client* pClient = to_who->GetPilot();
585  if (pClient == nullptr)
586  return;
587  if (is_log_enabled(DESTINY__BUBBLE_TRACE))
588  PrintEntityList();
589 
590  Buffer* destinyBuffer = new Buffer();
591 
593  head.packet_type = 1; // 0 = full state 1 = balls
594  head.stamp = sEntityList.GetStamp();
595  destinyBuffer->Append(head);
596 
597  AddBalls2 addballs2;
598  addballs2.stateStamp = sEntityList.GetStamp();
599  addballs2.extraBallData = new PyList();
600 
601  for (auto cur : m_dynamicEntities) {
602  if (cur.second->IsMissileSE() or cur.second->IsContainerSE()) {
603  addballs2.extraBallData->AddItem(cur.second->MakeSlimItem());
604  } else {
605  PyTuple* balls = new PyTuple(2);
606  balls->SetItem(0, cur.second->MakeSlimItem());
607  balls->SetItem(1, cur.second->MakeDamageState());
608  addballs2.extraBallData->AddItem(balls);
609  }
610  cur.second->EncodeDestiny(*destinyBuffer);
611  }
612 
613  if (addballs2.extraBallData->size() < 1) {
614  SafeDelete( destinyBuffer );
615  return;
616  }
617 
618  addballs2.state = new PyBuffer(&destinyBuffer); //consumed
619  SafeDelete( destinyBuffer );
620 
621  _log( DESTINY__MESSAGE, "SystemBubble::SendAddBalls2() to %s", pClient->GetName());
622  if (is_log_enabled(DESTINY__BALL_DUMP))
623  addballs2.Dump( DESTINY__BALL_DUMP, " " );
624  //_log( DESTINY__TRACE, " Ball Binary:" );
625  //_hex( DESTINY__TRACE, &( addballs2.state->content() )[0], (uint32)addballs2.state->content().size() );
626  /* note: this shows up in valgrind as an uninitialized value -allan 24Mar16
627  * Conditional jump or move depends on uninitialised value(s) SystemBubble.cpp:484 (uncorrected line#)
628  * Uninitialised value was created by a heap allocation SystemBubble.cpp:472
629  */
630  _log( DESTINY__BALL_DECODE, " Ball Decoded:" );
631  if (is_log_enabled(DESTINY__BALL_DECODE))
632  Destiny::DumpUpdate( DESTINY__BALL_DECODE, &( addballs2.state->content() )[0], (uint32)addballs2.state->content().size() );
633  PyTuple* t = addballs2.Encode();
634  pClient->QueueDestinyUpdate(&t, true); //consumed
635 }
636 
638  if (!m_system->IsLoaded())
639  return;
640  if (pSE->DestinyMgr() != nullptr)
641  if (pSE->DestinyMgr()->IsCloaked())
642  return;
643 
644  Buffer* destinyBuffer = new Buffer();
645 
646  //create AddBalls header
648  head.packet_type = 1; // 0 = full state 1 = balls
649  head.stamp = sEntityList.GetStamp();
650  destinyBuffer->Append( head );
651 
652  AddBalls addballs;
653  //encode destiny binary
654  pSE->EncodeDestiny( *destinyBuffer );
655  addballs.state = new PyBuffer( &destinyBuffer );
656  //encode damage state
657  addballs.damageDict[ pSE->GetID() ] = pSE->MakeDamageState();
658  //encode SlimItem
659  addballs.slims = new PyList();
660  addballs.slims->AddItem( new PyObject( "foo.SlimItem", pSE->MakeSlimItem() ) );
661 
662  _log(DESTINY__BUBBLE_TRACE, "SystemBubble::AddBallExclusive() - Adding entity %u to bubble %u", pSE->GetID(), m_bubbleID);
663  if (is_log_enabled(DESTINY__BALL_DUMP))
664  addballs.Dump( DESTINY__BALL_DUMP, " " );
665  _log( DESTINY__BALL_DECODE, " Ball Decoded:" );
666  if (is_log_enabled(DESTINY__BALL_DECODE))
667  Destiny::DumpUpdate( DESTINY__BALL_DECODE, &( addballs.state->content() )[0], (uint32)addballs.state->content().size() );
668  //bubblecast the update
669  PyTuple* t = addballs.Encode();
670  BubblecastDestinyUpdateExclusive( &t, "AddBall", pSE );
671  PySafeDecRef( t );
672 }
673 
674 /* NOTE lil insight into clients code for RemoveBall
675  * RemoveBall is function to remove all data associated with a particular ballID.
676  * this call is only effective when a SlimItem for that ball is currently active in clients bubble,
677  * and the ballID is > destiny.dstLocalBalls (which i dont know exactly what that is yet)
678  * RemoveBalls is called when there is an associated TerminalExplosion with that ballID
679  * [code]
680  * if funcName == 'RemoveBalls':
681  exploders = [ x[1][1][0] for x in state if x[1][0] == 'TerminalExplosion' ]
682  * [/code]
683  * RemoveBalls is then called on the entire group, and will call RemoveBall(ballID, terminal) on each ball.
684  * the bool 'terminal' is initially false, then set to true if there is an associated TerminalExplosion for that ballID.
685  *
686  * see also DestinyManager::SendTerminalExplosion()
687  * NOTE RemoveBall doesnt not work as i thought it should....doesnt trigger explosion.
688  */
689 //TODO update these based on above notes (also look into better (non-ambigious) naming)
691  // RemoveBallFromBP removeball;
692  // removeball.entityID = about_who->GetID();
693  // using RemoveBalls instead of RemoveBall because client
694  // seems not to trigger explosion on RemoveBall
695  if (!m_system->IsLoaded())
696  return;
697  RemoveBallsFromBP removeball;
698  removeball.balls.push_back(about_who->GetID());
699 
700  _log(DESTINY__MESSAGE, "SystemBubble::RemoveBall()");
701  if (is_log_enabled(DESTINY__BALL_DUMP))
702  removeball.Dump( DESTINY__BALL_DUMP, " " );
703 
704  PyTuple *tmp = removeball.Encode();
705  BubblecastDestinyUpdate(&tmp, "RemoveBall");
706  PySafeDecRef( tmp );
707 }
708 
709 // this *should* only be called from DestinyMgr::Cloak() and DestinyMgr::Jump()
711  RemoveBallFromBP removeball;
712  removeball.entityID = about_who->GetID();
713  // RemoveBalls removeball;
714  //removeball.balls.push_back(about_who->GetID());
715 
716  _log(DESTINY__MESSAGE, "SystemBubble::RemoveBallExclusive()");
717  if (is_log_enabled(DESTINY__BALL_DUMP))
718  removeball.Dump( DESTINY__BALL_DUMP, " " );
719 
720  PyTuple *tmp = removeball.Encode();
721  BubblecastDestinyUpdateExclusive(&tmp, "RemoveBall", about_who);
722  PySafeDecRef( tmp );
723 }
724 
726  if (!m_system->IsLoaded())
727  return;
728  if (m_dynamicEntities.empty())
729  return;
730  if ((!to_who->HasPilot()) or (to_who->SysBubble() == nullptr))
731  return;
732  Client* pClient = to_who->GetPilot();
733  if ((pClient == nullptr) or pClient->IsDock() or pClient->IsDocked())
734  return;
735 
736  RemoveBallsFromBP remove_balls;
737 
738  for (auto cur : m_dynamicEntities)
739  remove_balls.balls.push_back(cur.first);
740 
741  if (remove_balls.balls.empty())
742  return;
743 
744  _log( DESTINY__MESSAGE, "SystemBubble::RemoveBalls() - sending to %s", pClient->GetName());
745  if (is_log_enabled(DESTINY__BALL_DUMP))
746  remove_balls.Dump( DESTINY__BALL_DUMP, " " );
747 
748  PyTuple* tmp = remove_balls.Encode();
749  pClient->QueueDestinyUpdate( &tmp );
750 }
751 
753 {
754  PyList* header = new PyList(7);
755  header->SetItemString(0, "droneID");
756  header->SetItemString(1, "ownerID");
757  header->SetItemString(2, "controllerID");
758  header->SetItemString(3, "activityState");
759  header->SetItemString(4, "typeID");
760  header->SetItemString(5, "controllerOwnerID");
761  header->SetItemString(6, "targetID");
762  PyList* lines = new PyList();
763  for (auto cur : m_drones) {
764  PyList* line = new PyList(7);
765  line->SetItem(0, new PyInt(cur.first));
766  line->SetItem(1, new PyInt(cur.second->GetOwnerID()));
767  line->SetItem(2, new PyInt(cur.second->GetControllerID()));
768  line->SetItem(3, new PyInt(cur.second->GetState()));
769  line->SetItem(4, new PyInt(cur.second->GetSelf()->typeID()));
770  line->SetItem(5, new PyInt(cur.second->GetControllerOwnerID()));
771  line->SetItem(6, new PyInt(cur.second->GetTargetID()));
772  lines->AddItem(line);
773  }
774 
775  PyDict* dict = new PyDict();
776  dict->SetItemString("header", header);
777  dict->SetItemString("RowClass", new PyToken("util.Row"));
778  dict->SetItemString("lines", lines);
779 
780  return new PyObject("util.Rowset", dict);
781 }
782 
784  // send positions of all dSE in bubble to all players in bubble
785  for (auto player : m_players)
786  for (auto dse : m_dynamicEntities) {
787  SetBallPosition du;
788  du.entityID = dse.first;
789  du.x = dse.second->GetPosition().x;
790  du.y = dse.second->GetPosition().y;
791  du.z = dse.second->GetPosition().z;
792  PyTuple* up = du.Encode();
793  player.second->GetShipSE()->DestinyMgr()->SendSingleDestinyUpdate(&up);
794  }
795 }
796 
798 {
799  for (auto dse : m_dynamicEntities) {
800  if (dse.second->IsNPCSE())
801  dse.second->GetNPCSE()->CmdDropLoot();
802  }
803 }
804 
805 
807 {
808  if (m_hasMarkers)
809  for (auto cur : m_markers) {
810  m_system->RemoveEntity(cur.second);
811  cur.second->Delete(); // delete marker cans here
812  SafeDelete(cur.second);
813  }
814  m_markers.clear();
815  m_centerSE = nullptr;
816  m_hasMarkers = false;
817 }
818 
819 
821 {
822  // we are not creating markers on system boot.
823  if (!m_system->IsLoaded())
824  return;
825  if (m_hasMarkers)
826  return;
827  // create jetcan to mark bubble center
828  std::string str = "Center Marker for Bubble #", desc = "Bubble Center"; //std::to_string(m_bubbleID);
829  str += std::to_string(m_bubbleID);
830  MarkBubble(m_center, str, desc, true);
831 
832  // create jetcan to mark bubble x
833  GPoint center = m_center;
834  center.x += BUBBLE_RADIUS_METERS - 5;
835  str.clear();
836  str = "Bubble #";
837  str += std::to_string(m_bubbleID);
838  str += " +X";
839  desc = "Bubble x";
840  MarkBubble(center, str, desc);
841 
842  // create jetcan to mark bubble -x
843  center = m_center;
844  center.x -= BUBBLE_RADIUS_METERS - 5;
845  str.clear();
846  str = "Bubble #";
847  str += std::to_string(m_bubbleID);
848  str += " -X";
849  desc = "Bubble -x";
850  MarkBubble(center, str, desc);
851 
852  // create jetcan to mark bubble y
853  center = m_center;
854  center.y += BUBBLE_RADIUS_METERS - 5;
855  str.clear();
856  str = "Bubble #";
857  str += std::to_string(m_bubbleID);
858  str += " +Y";
859  desc = "Bubble y";
860  MarkBubble(center, str, desc);
861 
862  // create jetcan to mark bubble -y
863  center = m_center;
864  center.y -= BUBBLE_RADIUS_METERS - 5;
865  str.clear();
866  str = "Bubble #";
867  str += std::to_string(m_bubbleID);
868  str += " -Y";
869  desc = "Bubble -y";
870  MarkBubble(center, str, desc);
871 
872  // create jetcan to mark bubble z
873  center = m_center;
874  center.z += BUBBLE_RADIUS_METERS - 5;
875  str.clear();
876  str = "Bubble #";
877  str += std::to_string(m_bubbleID);
878  str += " +Z";
879  desc = "Bubble z";
880  MarkBubble(center, str, desc);
881 
882  // create jetcan to mark bubble -z
883  center = m_center;
884  center.z -= BUBBLE_RADIUS_METERS - 5;
885  str.clear();
886  str = "Bubble #";
887  str += std::to_string(m_bubbleID);
888  str += " -Z";
889  desc = "Bubble -z";
890  MarkBubble(center, str, desc);
891 
892  m_hasMarkers = true;
893 }
894 
895 void SystemBubble::MarkBubble(const GPoint& position, std::string& name, std::string& desc, bool center/*false*/)
896 {
897  // create new container item
898  ItemData idata(23, ownerSystem, m_systemID, flagNone, name.c_str(), position, desc.c_str());
900  if ( cRef.get() == nullptr) {
901  _log(DESTINY__WARNING, "MarkBubble() could not create Item for %s (%s)", name.c_str(), desc.c_str());
902  return;
903  }
904 
905  // create SE for item
906  FactionData jetcanData = FactionData();
907  ContainerSE* cSE = new ContainerSE( cRef, *(m_system->GetServiceMgr()), m_system, jetcanData);
908  if (cSE == nullptr) {
909  _log(DESTINY__WARNING, "MarkBubble() could not create SE for %s (%s)", name.c_str(), desc.c_str());
910  return;
911  }
912  cRef->SetMySE(cSE);
913  cSE->AnchorContainer();
914  if (center) {
915  // only setting centers as global
916  cSE->SetGlobal(true);
917  m_centerSE = cSE;
918  }
919  m_markers.emplace( cRef->itemID(), cSE);
920  m_system->AddEntity(cSE, center);
921 }
922 
923 
924 void SystemBubble::BubblecastDestiny(std::vector<PyTuple *> &updates, std::vector<PyTuple *> &events, const char *desc) const {
925  if (m_players.empty())
926  return;
927 
928  BubblecastDestinyUpdate(updates, desc);
929  BubblecastDestinyEvent(events, desc);
930 }
931 
932 void SystemBubble::BubblecastDestinyUpdate(std::vector<PyTuple *> &updates, const char *desc) const {
933  for (std::vector<PyTuple *>::iterator cur = updates.begin(); cur != updates.end(); ++cur)
934  BubblecastDestinyUpdate(&(*cur), desc);
935 
936  updates.clear();
937 }
938 
939 void SystemBubble::BubblecastDestinyEvent(std::vector<PyTuple *> &events, const char *desc) const {
940  for (std::vector<PyTuple *>::iterator cur = events.begin(); cur != events.end(); ++cur)
941  BubblecastDestinyEvent(&(*cur), desc);
942 
943  events.clear();
944 }
945 
946 void SystemBubble::BubblecastDestinyUpdate( PyTuple** payload, const char* desc ) const
947 {
948  if (is_log_enabled(DESTINY__BUBBLECAST_DUMP))
949  (*payload)->Dump(DESTINY__BUBBLECAST_DUMP, " ");
950  for (auto cur : m_players) {
951  _log( DESTINY__BUBBLECAST, "Bubblecast %s update to %s(%u)", desc, cur.second->GetName(), cur.first );
952  PyIncRef(*payload);
953  cur.second->QueueDestinyUpdate(payload);
954  }
955 }
956 
957 void SystemBubble::BubblecastDestinyUpdateExclusive( PyTuple** payload, const char* desc, SystemEntity* pSE ) const
958 {
959  for (auto cur : m_players) {
960  // Only queue a Destiny update for this bubble if the current SystemEntity is not 'pSE':
961  // (this is an update to all client objects in the bubble EXCLUDING 'pSE')
962  if (cur.second->GetShipSE() != pSE) {
963  _log( DESTINY__BUBBLECAST, "Exclusive Bubblecast %s update to %s(%u)", desc, cur.second->GetName(), cur.first );
964  PyIncRef(*payload);
965  cur.second->QueueDestinyUpdate(payload);
966  }
967  }
968 }
969 
970 void SystemBubble::BubblecastDestinyEvent( PyTuple** payload, const char* desc ) const
971 {
972  if (is_log_enabled(DESTINY__BUBBLECAST_DUMP))
973  (*payload)->Dump(DESTINY__BUBBLECAST_DUMP, " ");
974  for (auto cur : m_players) {
975  _log( DESTINY__BUBBLECAST, "Bubblecast %s event to %s(%u)", desc, cur.second->GetName(), cur.first );
976  PyIncRef(*payload);
977  cur.second->QueueDestinyEvent(payload);
978  }
979 }
980 
981 void SystemBubble::BubblecastSendNotification(const char* notifyType, const char* idType, PyTuple** payload, bool seq)
982 {
983  for (auto cur : m_players) {
984  _log( DESTINY__BUBBLECAST, "BubblecastNotify %s to %s(%u)", notifyType, cur.second->GetName(), cur.first );
985  PyIncRef(*payload);
986  cur.second->SendNotification( notifyType, idType, payload, seq );
987  }
988 }
void Append(const T &value)
Appends a single value to buffer.
Definition: Buffer.h:437
#define sConfig
A macro for easier access to the singleton.
void SetGlobal(bool set=false)
Definition: Container.h:166
virtual DroneSE * GetDroneSE()
Definition: SystemEntity.h:135
const GPoint m_center
Definition: SystemBubble.h:167
void MarkBubble(const GPoint &position, std::string &name, std::string &desc, bool center=false)
void AddEntity(SystemEntity *pSE, bool addSignal=true)
std::map< uint32, SystemEntity * > m_dynamicEntities
Definition: SystemBubble.h:194
uint16 m_bubbleID
Definition: SystemBubble.h:189
std::map< uint32, SystemEntity * > m_markers
Definition: SystemBubble.h:193
void GetEntityVec(std::vector< SystemEntity * > &into) const
void BubblecastDestinyUpdateExclusive(PyTuple **payload, const char *desc, SystemEntity *pSE) const
void AddBallExclusive(SystemEntity *about_who)
#define _log(type, fmt,...)
Definition: logsys.h:124
void SendAddBalls(SystemEntity *to_who)
void Disable()
Definition: timer.h:39
const double m_radius
Definition: SystemBubble.h:168
Python's dictionary.
Definition: PyRep.h:719
~SystemBubble() noexcept
void PrintEntityList()
SystemBubble * SysBubble()
Definition: SystemEntity.h:195
bool InBubble(const GPoint &pt, bool inWarp=false) const
void GetEntities(std::map< uint32, SystemEntity * > &into) const
void SendAddBalls2(SystemEntity *to_who)
void SetItemString(size_t index, const char *str)
Stores Python string.
Definition: PyRep.h:699
virtual bool HasPilot()
Definition: SystemEntity.h:258
virtual Client * GetPilot()
Definition: SystemEntity.h:259
SystemBubble * m_bubble
Definition: SystemEntity.h:262
int32 GetCharacterID() const
Definition: Client.h:113
void BubblecastSendNotification(const char *notifyType, const char *idType, PyTuple **payload, bool seq=true)
std::map< uint32, DroneSE * > m_drones
Definition: SystemBubble.h:196
#define sEntityList
Definition: EntityList.h:208
const float GetSystemSecurityRating()
Definition: SystemManager.h:86
uint32 GetID() const
Definition: SystemManager.h:80
void CmdDropLoot()
PyObject * GetDroneState() const
Python tuple.
Definition: PyRep.h:567
GaFloat x
Definition: GaTypes.h:207
SystemBubble(SystemManager *pSystem, const GPoint &center, double radius)
const GPoint & GetPosition() const
Definition: SystemEntity.h:211
void AddItem(PyRep *i)
Definition: PyRep.h:701
void SafeDelete(T *&p)
Deletes and nullifies a pointer.
Definition: SafeMem.h:83
SystemEntity * GetRandomEntity()
void AnchorContainer()
Definition: Container.cpp:305
#define is_log_enabled(type)
Definition: logsys.h:78
#define sLog
Evaluates to a NewLog instance.
Definition: LogNew.h:250
virtual PyDict * MakeSlimItem()
Definition: gpoint.h:33
void BubblecastDestiny(std::vector< PyTuple * > &updates, std::vector< PyTuple * > &events, const char *desc) const
DestinyManager * DestinyMgr()
Definition: SystemEntity.h:198
SystemManager * SystemMgr()
Definition: SystemEntity.h:196
void RemoveBallExclusive(SystemEntity *about_who)
void GetAllEntities(std::map< uint32, SystemEntity * > &into) const
Generic class for buffers.
Definition: Buffer.h:40
void SetGate(uint32 gateID)
void BubblecastDestinyEvent(std::vector< PyTuple * > &events, const char *desc) const
Python object.
Definition: PyRep.h:826
bool Enabled() const
Definition: timer.h:41
PyTuple * MakeDamageState()
void SetItem(size_t index, PyRep *object)
Stores Python object.
Definition: PyRep.h:610
bool IsDocked()
Definition: Client.h:229
static const GPoint NULL_ORIGIN(0, 0, 0)
void RemoveEntity(SystemEntity *pSE)
void SetBelt(InventoryItemRef itemRef)
Timer m_spawnTimer
Definition: SystemBubble.h:199
Python integer.
Definition: PyRep.h:231
void SetItem(size_t index, PyRep *object)
Stores Python object.
Definition: PyRep.h:682
BeltMgr * GetBeltMgr()
void SetSpawnTimer(bool isBelt=false)
static InventoryItemRef SpawnTemp(ItemData &data)
uint32 GetID()
Definition: SystemEntity.h:207
bool Check(bool reset=true)
Definition: timer.cpp:62
const char * GetName() const
Definition: Client.h:94
std::map< uint32, Client * > m_players
Definition: SystemBubble.h:192
const char * GetName() const
Definition: SystemEntity.h:210
void ResetBubbleRatSpawn()
bool IsOverlap(const GPoint &pt) const
void QueueDestinyUpdate(PyTuple **update, bool DoPackage=false, bool IsSetState=false)
Definition: Client.cpp:2131
Definition: Client.h:66
void RemoveExclusive(SystemEntity *pSE)
static RefPtr StaticCast(const RefPtr< Y > &oth)
Acts as static_cast from one RefPtr to another.
Definition: RefPtr.h:238
unsigned __int32 uint32
Definition: eve-compat.h:50
uint32 CountNPCs()
if(sConfig.world.saveOnMove)
#define PyIncRef(op)
Definition: PyRep.h:56
virtual bool isGlobal()
Definition: SystemEntity.h:142
PyServiceMgr * GetServiceMgr()
Definition: SystemManager.h:88
virtual void EncodeDestiny(Buffer &into)
Python token (eg. class name).
Definition: PyRep.h:522
GaFloat y
Definition: GaTypes.h:207
bool IsDock()
Definition: Client.h:230
void RemoveBall(SystemEntity *about_who)
int64 MakeRandomInt(int64 low, int64 high)
Generates random integer from interval [low; high].
Definition: misc.cpp:109
void ProcessWander(std::vector< SystemEntity * > &wanderers)
void Remove(SystemEntity *pSE)
uint32 m_systemID
Definition: SystemBubble.h:190
void DumpUpdate(LogType into, const uint8 *data, uint32 len)
void DoSpawnForBubble(SystemBubble *pBubble)
SystemEntity *const GetEntity(uint32 entityID) const
std::map< uint32, SystemEntity * > m_entities
Definition: SystemBubble.h:195
void RemoveBalls(SystemEntity *to_who)
ContainerSE * m_centerSE
Definition: SystemBubble.h:184
void traceStack(void)
Definition: misc.cpp:169
#define PySafeDecRef(op)
Definition: PyRep.h:61
SystemManager * m_system
Definition: SystemBubble.h:183
Python buffer.
Definition: PyRep.h:382
void BubblecastDestinyUpdate(std::vector< PyTuple * > &updates, const char *desc) const
Definition: gpoint.h:70
GaExpInl GaFloat distance(const GaVec3 &oth) const
Definition: GaTypes.h:158
void RegisterBelt(InventoryItemRef itemRef)
Definition: BeltMgr.cpp:57
virtual bool IsStaticEntity()
Definition: SystemEntity.h:146
#define sBubbleMgr
void Add(SystemEntity *pSE)
virtual bool IsDroneSE()
Definition: SystemEntity.h:187
uint16 typeID() const
Python list.
Definition: PyRep.h:639
GaFloat z
Definition: GaTypes.h:207
uint32 itemID() const
Definition: InventoryItem.h:98
void SetItemString(const char *key, PyRep *value)
SetItemString adds or sets a database entry.
Definition: PyRep.h:812
static const int64 ONE_AU_IN_METERS
Definition: EVE_Consts.h:40
void GetPlayers(std::vector< Client * > &into) const
void RemoveMarkers()
void CheckSpawn(uint16 bubbleID)
Definition: BeltMgr.cpp:82
void Start(uint32 setTimerTime=0, bool changeResetTimer=true)
Definition: timer.cpp:81
static const float BUBBLE_RADIUS_METERS
Definition: BubbleManager.h:32