Leviathan  0.8.0.0
Leviathan game engine
GameWorld.cpp
Go to the documentation of this file.
1 // ------------------------------------ //
2 #include "GameWorld.h"
3 
5 #include "Components.h"
6 #include "Engine.h"
7 #include "Handlers/IDFactory.h"
14 #include "Physics/PhysicalWorld.h"
16 #include "Rendering/Graphics.h"
18 #include "Script/ScriptExecutor.h"
19 #include "ScriptComponentHolder.h"
20 #include "ScriptSystemWrapper.h"
21 #include "Sound/SoundDevice.h"
23 #include "Window.h"
24 
25 // Camera interpolation
26 #include "Generated/ComponentStates.h"
27 #include "StateInterpolator.h"
28 
29 #include "Exceptions.h"
30 
31 #include "bsfCore/Components/BsCCamera.h"
32 #include "bsfCore/Components/BsCLight.h"
33 #include "bsfCore/Components/BsCSkybox.h"
34 #include "bsfCore/Scene/BsSceneObject.h"
35 
36 using namespace Leviathan;
37 // ------------------------------------ //
38 
39 // // Ray callbacks //
40 // static dFloat RayCallbackDataCallbackClosest(const NewtonBody* const body,
41 // const NewtonCollision* const shapeHit, const dFloat* const hitContact,
42 // const dFloat* const hitNormal, dLong collisionID, void* const userData,
43 // dFloat intersectParam);
44 
45 // ------------------------------------ //
47 public:
49  {
50  // Check that there are no external references to these
51  for(const auto& tuple : RegisteredScriptComponents) {
52 
53  if(tuple.second->GetRefCount() != 1) {
54 
55  LOG_FATAL("GameWorld: ImplRelease: RegisteredScriptComponent (holder): has "
56  "external refs, count: " +
57  std::to_string(tuple.second->GetRefCount()));
58  }
59  }
60  }
61 
62  std::map<std::string, ScriptComponentHolder::pointer> RegisteredScriptComponents;
63  std::map<std::string, std::unique_ptr<ScriptSystemWrapper>> RegisteredScriptSystems;
64 
66  std::vector<std::tuple<WantedClockType::time_point, ResponseEntityUpdate>>
68 
69  // BSF rendering resources
70  bs::HSceneObject WorldCameraSO;
71  bs::HCamera WorldCamera;
72 
73  bs::HSceneObject SunlightSO;
74  bs::HLight Sunlight;
75  bs::HSceneObject SkyboxSO;
76  bs::HSkybox Skybox;
77 
79  static int LayerNumber;
80 };
81 
83 
84 // ------------------------------------ //
85 DLLEXPORT GameWorld::GameWorld(int32_t worldtype,
86  const std::shared_ptr<PhysicsMaterialManager>& physicsMaterials, int worldid /*= -1*/) :
87  pimpl(std::make_unique<Implementation>()),
88  PhysicsMaterials(physicsMaterials), ID(worldid >= 0 ? worldid : IDFactory::GetID()),
89  WorldType(worldtype)
90 {}
91 
93 {
94  // (*WorldDestroyed) = true;
95 
96  // Assert if all objects haven't been released already.
97  // We can't call virtual methods here anymore
98  // This can be hit in tests quite easily if something throws an exception
100  Entities.empty(), "GameWorld: Entities not empty in destructor. Was Release called?");
101 }
102 // ------------------------------------ //
104 {
105  NetworkSettings = network;
106 
107  // Detecting non-GUI mode //
108  if(graphics) {
109 
110  GraphicalMode = true;
111  // these are always required for worlds //
112  _CreateRenderingResources(graphics);
113  } else {
114 
115  GraphicalMode = false;
116  InBackground = false;
117  }
118 
119  // Acquire physics engine world only if we have been given physical materials indicating
120  // that physics is wanted
121  if(PhysicsMaterials) {
122 
123  _PhysicalWorld = std::make_shared<PhysicalWorld>(this, PhysicsMaterials.get());
124  }
125 
126  _DoSystemsInit();
127  return true;
128 }
129 
131 {
133 
134  // (*WorldDestroyed) = true;
135 
136  ReceivingPlayers.clear();
137 
138  // release objects //
139  // TODO: allow objects to know that they are about to get killed
140 
141  // As all objects are just pointers to components we can just dump the objects
142  // and once the component pools are released
143  ClearEntities();
144 
145  if(GraphicalMode) {
146  // TODO: notify our window that it no longer has anything rendering on it
147  LinkedToWindow = nullptr;
148 
149  _DestroyRenderingResources();
150  }
151 
152  // This should be relatively cheap if the newton threads don't deadlock while waiting
153  // for each other
154  _PhysicalWorld.reset();
155 
156  // Let go of our these resources
157  pimpl.reset();
158 }
159 // ------------------------------------ //
160 void GameWorld::_CreateRenderingResources(Graphics* graphics)
161 {
162  // // create scene manager //
163  // // Let's do what the Ogre samples do and use a bunch of threads for culling
164  // const auto threads = std::max(2, static_cast<int>(std::thread::hardware_concurrency()));
165 
166  // // TODO: allow configuring scene type (the type was: Ogre::ST_EXTERIOR_FAR before)
167 
168  // Scene setup (TODO: redo once bsf has multiple scenes)
169  BSFLayerHack = Implementation::LayerNumber++;
170 
171  // Camera
172  pimpl->WorldCameraSO =
173  bs::SceneObject::create(("Camera_World_" + std::to_string(ID)).c_str());
174  pimpl->WorldCamera = pimpl->WorldCameraSO->addComponent<bs::CCamera>();
175  pimpl->WorldCamera->setHorzFOV(bs::Degree(90));
176 
177  pimpl->WorldCamera->setLayers(1 << *GetScene());
178 
179 
180  // TODO: allow changing and setting infinite
181  pimpl->WorldCamera->setFarClipDistance(5000);
182  // // enable infinite far clip distance if supported //
183  // if(ogre->getRenderSystem()->getCapabilities()->hasCapability(
184  // Ogre::RSC_INFINITE_FAR_PLANE)) {
185 
186  // WorldSceneCamera->setFarClipDistance(0); Maybe for bsf this needs to be float::max
187  // }
188 
189  auto values = Engine::Get()->GetDefinition()->GetValues();
190 
191 
192  // TODO: move to a better settings class and realtime updates for settings
193  bool disableIndirectLighting, disableAmbientOcclusion, disableFXAA, disableShadows,
194  disableLighting;
195 
196  ObjectFileProcessor::LoadValueFromNamedVars<bool>(
197  values, "DisableIndirectLighting", disableIndirectLighting, false);
198  ObjectFileProcessor::LoadValueFromNamedVars<bool>(
199  values, "DisableAmbientOcclusion", disableAmbientOcclusion, false);
200  ObjectFileProcessor::LoadValueFromNamedVars<bool>(
201  values, "DisableFXAA", disableFXAA, false);
202  ObjectFileProcessor::LoadValueFromNamedVars<bool>(
203  values, "DisableShadows", disableShadows, false);
204  ObjectFileProcessor::LoadValueFromNamedVars<bool>(
205  values, "DisableLighting", disableLighting, false);
206 
207  int MSAACount;
208 
209  ObjectFileProcessor::LoadValueFromNamedVars<int>(values, "MSAACount", MSAACount, 1);
210 
211 
212  pimpl->WorldCamera->setMSAACount(1);
213 
214  const auto& settings = pimpl->WorldCamera->getRenderSettings();
215 
216  // Needed. non default option
217  if(!disableIndirectLighting) {
218  settings->enableIndirectLighting = true;
219  } else {
220  settings->enableIndirectLighting = false;
221  }
222 
223  if(disableAmbientOcclusion)
224  settings->ambientOcclusion.enabled = false;
225 
226  // settings->autoExposure;
227  // settings->bloom;
228  // settings->bloom.enabled = false;
229  // settings->colorGrading;
230  // settings->depthOfField.enabled = false;
231  // settings->screenSpaceLensFlare;
232  // settings->screenSpaceReflections;
233  // settings->shadowSettings;
234  // settings->tonemapping;
235  // settings->whiteBalance;
236  // settings->enableAutoExposure = false;
237  if(disableFXAA)
238  settings->enableFXAA = false;
239  // settings->enableHDR = false;
240  if(disableLighting)
241  settings->enableLighting = false;
242 
243  if(disableShadows)
244  settings->enableShadows = false;
245  // settings->enableSkybox = false;
246  // settings->enableTonemapping = false;
247 
248 
249  pimpl->WorldCamera->setRenderSettings(settings);
250 
251  // Default sun
252  SetSunlight();
253 }
254 
255 void GameWorld::_DestroyRenderingResources()
256 {
257  if(!pimpl)
258  return;
259 
260  RemoveSunlight();
261 
262  if(pimpl->WorldCameraSO) {
263  pimpl->WorldCameraSO->destroy();
264  pimpl->WorldCameraSO = nullptr;
265  pimpl->WorldCamera = nullptr;
266  }
267 }
268 // ------------------------------------ //
269 static bool SunCreated = false;
270 
272 {
273  if(SunCreated) {
274  LOG_WRITE("TODO: multi scene support in BSF needed for separate world lights");
275  return;
276  }
277 
278  SunCreated = true;
279 
280  // Create/update things if they are nullptr //
281  if(!pimpl->SunlightSO) {
282 
283  pimpl->SunlightSO = bs::SceneObject::create("Sunlight");
284  pimpl->Sunlight = pimpl->SunlightSO->addComponent<bs::CLight>();
285  // Oh no! this method does not exist
286  // pimpl->Sunlight->setLayer
287  }
288 
289  // Default properties
290  pimpl->Sunlight->setType(bs::LightType::Directional);
291 
292  SetLightProperties(Float3(1, 1, 1));
293 }
294 
296 {
297  if(pimpl->SunlightSO) {
298  pimpl->SunlightSO->destroy();
299  pimpl->SunlightSO = nullptr;
300  pimpl->Sunlight = nullptr;
301  SunCreated = false;
302  }
303 }
304 
305 DLLEXPORT void GameWorld::SetSkybox(const std::string& skyboxname, float brightness /*= 1.f*/)
306 {
307  if(!pimpl->SkyboxSO) {
308  if(skyboxname.empty())
309  return;
310 
311  pimpl->SkyboxSO = bs::SceneObject::create("Skybox");
312  pimpl->Skybox = pimpl->SkyboxSO->addComponent<bs::CSkybox>();
313  // Oh no! this method does not exist
314  // pimpl->Skybox->setLayer
315  }
316 
317  if(!skyboxname.empty()) {
318 
319  auto texture = Engine::Get()->GetGraphics()->LoadTextureByName(skyboxname);
320 
321  if(!texture) {
322 
323  LOG_ERROR("GameWorld: SetSkybox: could not load skybox texture with the name: " +
324  skyboxname);
325  return;
326  }
327 
328  pimpl->Skybox->setTexture(texture);
329  pimpl->Skybox->setBrightness(brightness);
330 
331  } else {
332 
333  pimpl->Skybox->setTexture(nullptr);
334  pimpl->Skybox->setBrightness(0);
335  }
336 }
337 
338 DLLEXPORT void GameWorld::SetLightProperties(const Float3& colour, float intensity,
339  const Float3& direction, float sourceradius, bool castsshadows)
340 {
341  if(!pimpl->SunlightSO) {
342 
343  LOG_ERROR("GameWorld: SetLightProperties: world doesn't have sun light set");
344  return;
345  }
346 
347  pimpl->Sunlight->setColor(bs::Color(colour.X, colour.Y, colour.Z));
348  pimpl->Sunlight->setIntensity(intensity);
349  pimpl->Sunlight->setSourceRadius(sourceradius);
350  pimpl->Sunlight->setCastsShadow(castsshadows);
351 
352  // pimpl->SunlightSO->setPosition(bs::Vector3(1, 20, 1));
353  // pimpl->SunlightSO->setPosition(bs::Vector3(20, 15, 20));
354  pimpl->SunlightSO->setPosition(-direction);
355 
356  pimpl->SunlightSO->lookAt(bs::Vector3(0, 0, 0));
357  // pimpl->SunlightSO->setWorldRotation(const Quaternion &rotation)
358 
359  // TODO: scene ambient colour
360 
361  // Set scene ambient colour //
362  // TODO: Ogre samples also use this so maybe this works with PBR HLMS system
363  // WorldsScene->setAmbientLight(Ogre::ColourValue(0.3f, 0.5f, 0.7f) * 0.1f * 0.75f,
364  // Ogre::ColourValue(0.6f, 0.45f, 0.3f) * 0.065f * 0.75f,
365  // -Sunlight->getDirection() + Ogre::Vector3::UNIT_Y * 0.2f);
366 }
367 
368 // ------------------------------------ //
369 DLLEXPORT void GameWorld::Render(int mspassed, int tick, int timeintick)
370 {
371  if(InBackground) {
372 
373  LOG_ERROR("GameWorld: Render: called while world is in the background (not attached"
374  "to a window)");
375  return;
376  }
377 
378  RunFrameRenderSystems(tick, timeintick);
379 
380  // Read camera entity and update position //
381 
382  // Skip if no camera //
383  if(CameraEntity == 0)
384  return;
385 
386  try {
387  Camera& properties = GetComponent<Camera>(CameraEntity);
388 
389  Position& position = GetComponent<Position>(CameraEntity);
390 
391  auto& states = GetStatesFor<Position>();
392 
393  // set camera position //
394  const auto interpolated =
395  StateInterpolator::Interpolate(states, CameraEntity, &position, tick, timeintick);
396 
397  if(!std::get<0>(interpolated)) {
398 
399  // No interpolated pos //
400  pimpl->WorldCameraSO->setPosition(position.Members._Position);
401  pimpl->WorldCameraSO->setRotation(position.Members._Orientation);
402 
403  } else {
404 
405  const auto& interpolatedPos = std::get<1>(interpolated);
406  pimpl->WorldCameraSO->setPosition(interpolatedPos._Position);
407  pimpl->WorldCameraSO->setRotation(interpolatedPos._Orientation);
408  }
409 
410  if(properties.SoundPerceiver) {
411 
413  position.Members._Position, position.Members._Orientation);
414  }
415 
416  if(properties.Marked || AppliedCameraPropertiesPtr != &properties) {
417 
418  AppliedCameraPropertiesPtr = &properties;
419 
420  pimpl->WorldCamera->setHorzFOV(bs::Degree(properties.FOV));
421 
422  properties.Marked = false;
423  }
424 
425  } catch(const Exception& e) {
426 
427  LOG_ERROR("GameWorld: Render: camera update failed. Was a component removed?, "
428  "exception:");
429  e.PrintToLog();
430  CameraEntity = 0;
431  return;
432  }
433 }
434 // ------------------------------------ //
436 {
437  CameraEntity = object;
438 
439  AppliedCameraPropertiesPtr = nullptr;
440 
441  if(CameraEntity == NULL_OBJECT)
442  return;
443 
444  // Check components //
445  try {
446  GetComponent<Camera>(object);
447  } catch(const NotFound&) {
448 
449  throw InvalidArgument("SetCamera object is missing a needed component (Camera)");
450  }
451 
452  try {
453  GetComponent<Position>(object);
454  } catch(const NotFound&) {
455 
456  throw InvalidArgument("SetCamera object is missing a needed component (Position)");
457  }
458 }
459 
460 DLLEXPORT bs::Ray GameWorld::CastRayFromCamera(int x, int y) const
461 {
462  // Fail if there is no active camera //
463  if(CameraEntity == NULL_OBJECT)
464  throw InvalidState("This world has no active CameraEntity");
465 
466  if(!pimpl->WorldCamera)
467  throw InvalidState("This world has no initialized camera resources");
468 
469  // Read the latest set data from the camera
470  // TODO: could jump to the actual latest position here if wanted
471  return pimpl->WorldCamera->screenPointToRay(bs::Vector2I(x, y));
472 }
473 
475 {
476  return pimpl->WorldCameraSO;
477 }
478 // ------------------------------------ //
480  Position& atposition, Connection& connection)
481 {
482  return true;
483 }
484 
486 {
487  for(auto& player : ReceivingPlayers) {
488 
489  if(player->GetConnection().get() == &connection) {
490 
491  return true;
492  }
493  }
494 
495  return false;
496 }
497 
498 DLLEXPORT void GameWorld::SetPlayerReceiveWorld(std::shared_ptr<ConnectedPlayer> ply)
499 {
500  // Skip if already added //
501  for(auto& player : ReceivingPlayers) {
502 
503  if(player == ply) {
504 
505  return;
506  }
507  }
508 
509  LOG_INFO("GameWorld: player(\"" + ply->GetNickname() + "\") is now receiving world");
510 
511  // Add them to the list of receiving players //
512  ReceivingPlayers.push_back(ply);
513 
514  if(!ply->GetConnection()->IsValidForSend()) {
515 
516  // The closing should be handled by somebody else
517  Logger::Get()->Error("GameWorld: requested to sync with a player who has closed their "
518  "connection");
519 
520  return;
521  }
522 
523  {
524  // Start world receive information
525  ply->GetConnection()->SendPacketToConnection(
526  std::make_shared<ResponseStartWorldReceive>(0, ID, WorldType),
528  }
529 
530  // Update the position data //
531  UpdatePlayersPositionData(*ply);
532 
533  // Start sending initial update //
534  Logger::Get()->Info(
535  "Starting to send " + Convert::ToString(Entities.size()) + " to player");
536 
537  // Now we can queue all objects for sending //
538  // TODO: make sure that all objects are sent
539  // TODO: redo this inside the world tick
540  // ThreadingManager::Get()->QueueTask(
541  // new RepeatCountedTask(std::bind<void>([](
542  // std::shared_ptr<Connection> connection,
543  // std::shared_ptr<ConnectedPlayer> processingobject, GameWorld* world,
544  // std::shared_ptr<bool> WorldInvalid)
545  // -> void
546  // {
547  // // Get the next object //
548  // RepeatCountedTask* task =
549  // dynamic_cast<RepeatCountedTask*>(TaskThread::GetThreadSpecificThreadObject()->
550  // QuickTaskAccess.get());
551 
552  // LEVIATHAN_ASSERT(task, "wrong type passed to our task");
553 
554  // size_t num = task->GetRepeatCount();
555 
556  // if(*WorldInvalid){
557 
558  // taskstopprocessingobjectsforinitialsynclabel:
559 
560  // // Stop processing //
561  // task->StopRepeating();
562  // return;
563  // }
564 
565  // // Stop if out of bounds //
566  // if(num >= world->Entities.size()){
567 
568  // goto taskstopprocessingobjectsforinitialsynclabel;
569  // }
570 
571  // // Get the object //
572  // auto tosend = world->Entities[num];
573 
574  // // Skip if shouldn't send //
575  // try{
576 
577  // auto& position = world->GetComponent<Position>(tosend);
578 
579  // if(!world->ShouldPlayerReceiveObject(position, *connection)){
580 
581  // return;
582  // }
583 
584  // } catch(const NotFound&){
585 
586  // // No position, should probably always send //
587  // }
588 
589 
590  // // Send it //
591  // world->SendObjectToConnection(guard, tosend, connection);
592 
593  // return;
594 
595  // }, ply->GetConnection(), ply, this, WorldDestroyed), Entities.size()));
596 }
597 
599  const std::shared_ptr<NetworkResponse>& response, RECEIVE_GUARANTEE guarantee) const
600 {
601  // Notify everybody that an entity has been destroyed //
602  for(auto iter = ReceivingPlayers.begin(); iter != ReceivingPlayers.end(); ++iter) {
603 
604  auto safe = (*iter)->GetConnection();
605 
606  if(!safe->IsValidForSend()) {
607  // Player has probably closed their connection //
608  continue;
609  }
610 
611  safe->SendPacketToConnection(response, guarantee);
612  }
613 }
614 // ------------------------------------ //
616 
618 {
619  return 0;
620 }
621 
623  ObjectID id, sf::Packet& data, int entriesleft, int decodedtype)
624 {
625  if(entriesleft < 1)
626  return;
627 
628  LOG_ERROR("GameWorld: entity static state decoding was not complete before calling base "
629  "GameWorld implementation. Received entity won't be fully constructed");
630 }
631 
633  ObjectID id, int32_t ticknumber, sf::Packet& data, int32_t referencetick, int decodedtype)
634 {
635  LOG_ERROR(
636  "GameWorld: entity component state decoding was not complete before calling base "
637  "GameWorld implementation. Not all states have been decoded");
638 }
639 
641  ObjectID id, int32_t ticknumber, sf::Packet& data, int32_t referencetick, int decodedtype)
642 {
643  LOG_ERROR("GameWorld: entity component state decoding for local control was not complete "
644  "before calling base GameWorld implementation");
645 }
646 // ------------------------------------ //
647 DLLEXPORT void GameWorld::Tick(int currenttick)
648 {
649  if(InBackground && !TickWhileInBackground && GraphicalMode)
650  return;
651 
652  TickNumber = currenttick;
653 
654  // Apply queued packets //
656 
657  _HandleDelayedDelete();
658 
659  // All required nodes for entities are created //
662 
663  // Remove closed player connections //
664 
665  for(auto iter = ReceivingPlayers.begin(); iter != ReceivingPlayers.end();) {
666 
667  if(!(*iter)->GetConnection()->IsValidForSend()) {
668 
669  LOG_INFO("GameWorld: a player has diconnected, removing. TODO: release Sendable "
670  "memory");
671  // DEBUG_BREAK;
672  iter = ReceivingPlayers.erase(iter);
673  } else {
674 
675  ++iter;
676  }
677  }
678 
679  // Set this to disallow deleting while running physics as well
680  TickInProgress = true;
681 
682  // Simulate physics //
683  if(!WorldFrozen) {
684 
685  // TODO: a game type that is a client and server at the same time
686  // if(IsOnServer) {
687 
688  // _ApplyEntityUpdatePackets();
689  if(_PhysicalWorld)
690  _PhysicalWorld->SimulateWorld(TICKSPEED / 1000.f);
691 
692  // } else {
693 
694  // Simulate direct control //
695  // }
696  }
697 
698  _RunTickSystems();
699 
700  TickInProgress = false;
701 
702  // Sendable objects may need something to be done //
703 
704  if(NetworkSettings.IsAuthoritative) {
705 
706  // Notify new entities //
707  // DEBUG_BREAK;
708 
709  // Skip if not tick that will be stored //
710  // if(TickNumber % WORLD_OBJECT_UPDATE_CLIENTS_INTERVAL == 0){
711 
712  // _SendableSystem.Run(ComponentSendable.GetIndex(), *this);
713  // }
714 
715  } else {
716 
717  // TODO: direct control objects
718  // _ReceivedSystem.Run(ComponentReceived.GetIndex(), *this);
719  }
720 }
721 // ------------------------------------ //
723 {
724  // We are responsible for script systems //
725  for(auto iter = pimpl->RegisteredScriptSystems.begin();
726  iter != pimpl->RegisteredScriptSystems.end(); ++iter) {
727 
728  iter->second->CreateAndDestroyNodes();
729  }
730 }
731 
733 {
734  // We are responsible for script components //
735  for(auto iter = pimpl->RegisteredScriptComponents.begin();
736  iter != pimpl->RegisteredScriptComponents.end(); ++iter) {
737 
738  iter->second->ClearAdded();
739  iter->second->ClearRemoved();
740  }
741 }
742 // ------------------------------------ //
744 {
745  // Skip double Release
746  if(!pimpl)
747  return;
748 
749  // We are responsible for script systems //
750  for(auto iter = pimpl->RegisteredScriptSystems.begin();
751  iter != pimpl->RegisteredScriptSystems.end(); ++iter) {
752 
753  iter->second->Clear();
754  }
755 }
756 
758 {
759  // Skip double Release
760  if(!pimpl)
761  return;
762 
763  // We are responsible for script components //
764  for(auto iter = pimpl->RegisteredScriptComponents.begin();
765  iter != pimpl->RegisteredScriptComponents.end(); ++iter) {
766 
767  iter->second->ReleaseAllComponents();
768  }
769 }
770 // ------------------------------------ //
771 DLLEXPORT void GameWorld::RunFrameRenderSystems(int tick, int timeintick)
772 {
773  // Don't have any systems, but these updates may be important for interpolation //
774  // _ApplyEntityUpdatePackets();
775 
776  // TODO: if there are any impactful simulation done here it needs to be also inside a block
777  // where TickInProgress is set to true
778 }
779 
781 {
782  // We are responsible for script systems //
783  for(auto iter = pimpl->RegisteredScriptSystems.begin();
784  iter != pimpl->RegisteredScriptSystems.end(); ++iter) {
785 
786  iter->second->Run();
787  }
788 }
789 // ------------------------------------ //
791 {
792  float progress = Engine::Get()->GetTimeSinceLastTick() / (float)TICKSPEED;
793 
794  if(progress < 0.f)
795  return 0.f;
796 
797  return progress < 1.f ? progress : 1.f;
798 }
799 
800 DLLEXPORT std::tuple<int, int> GameWorld::GetTickAndTime() const
801 {
802  int tick = TickNumber;
803  int timeSince = static_cast<int>(Engine::Get()->GetTimeSinceLastTick());
804 
805  while(timeSince >= TICKSPEED) {
806 
807  ++tick;
808  timeSince -= TICKSPEED;
809  }
810 
811  return std::make_tuple(tick, timeSince);
812 }
813 
814 // ------------------ Object managing ------------------ //
816 {
817  if(!GetNetworkSettings().IsAuthoritative) {
818  // Clients create high number entities. This is not optimal but good enough for now
819  auto id = (1 << 31) | static_cast<ObjectID>(IDFactory::GetID());
820 
821  Entities.push_back(id);
822 
823  return id;
824 
825  } else {
826  auto id = static_cast<ObjectID>(IDFactory::GetID());
827 
828  Entities.push_back(id);
829 
830  if(NetworkSettings.IsAuthoritative) {
831  // NewlyCreatedEntities.push_back(id);
832 
833  if(NetworkSettings.AutoCreateNetworkComponents) {
835  }
836  }
837 
838  return id;
839  }
840 }
841 // ------------------------------------ //
843 {
844  // Release objects //
845  Entities.clear();
846  Parents.clear();
847  // This shouldn't be used all that much so release the memory
848  Parents.shrink_to_fit();
849 
850  // Clear all nodes //
851  _ResetSystems();
852 
853  // Clears all components
854  // Runs Release on components that need it
856 
857  // Notify everybody that all entities are discarded //
858  for(auto iter = ReceivingPlayers.begin(); iter != ReceivingPlayers.end(); ++iter) {
859 
860  auto safe = (*iter)->GetConnection();
861 
862  if(!safe->IsValidForSend()) {
863  // Player has probably closed their connection //
864  continue;
865  }
866 
867  Logger::Get()->Write("TODO: send world clear message");
868  DEBUG_BREAK;
869  }
870 }
871 // ------------------------------------ //
872 // DLLEXPORT Float3 GameWorld::GetGravityAtPosition(const Float3& pos)
873 // {
874 // // \todo take position into account //
875 // // create force without mass applied //
876 // return Float3(0.f, PHYSICS_BASE_GRAVITY, 0.f);
877 // }
878 // ------------------------------------ //
879 DLLEXPORT int GameWorld::GetPhysicalMaterial(const std::string& name)
880 {
881  if(!PhysicsMaterials)
882  return -1;
883 
884  return PhysicsMaterials->GetMaterialID(name);
885 }
886 // ------------------------------------ //
888 {
889  // Fail if trying to delete NULL_OBJECT
890  if(id == NULL_OBJECT)
891  throw InvalidArgument("Cannot destroy NULL_OBJECT");
892 
893  // Fail if ticking currently //
894  if(TickInProgress)
895  throw InvalidState(
896  "Cannot DestroyEntity while ticking. Use QueueDestroyEntity instead");
897 
898  for(auto iter = Entities.begin(); iter != Entities.end(); ++iter) {
899 
900  if(*iter == id) {
901 
902  Entities.erase(iter);
903  _DoDestroy(id);
904  return;
905  }
906  }
907 
908  LOG_ERROR("GameWorld: DestroyEntity: unknown entity id: " + std::to_string(id));
909 }
910 
912 {
913  // Fail if trying to delete NULL_OBJECT
914  if(id == NULL_OBJECT)
915  throw InvalidArgument("Cannot destroy NULL_OBJECT");
916 
917  // This is a sanity check, can be disabled (or made cheaper when the world no longer uses
918  // IDFactory) when crashing stops
919  bool exists = false;
920 
921  for(auto existingId : Entities) {
922  if(existingId == id) {
923  exists = true;
924  break;
925  }
926  }
927 
928  if(!exists) {
929  LOG_ERROR("GameWorld: QueueDestroyEntity: unknown entity id: " + std::to_string(id));
930  return;
931  }
932 
933  Lock lock(DeleteMutex);
934 
935  // Skip duplicates
936  for(auto alreadyQueued : DelayedDeleteIDS)
937  if(id == alreadyQueued)
938  return;
939 
940  DelayedDeleteIDS.push_back(id);
941 }
942 
943 void GameWorld::_HandleDelayedDelete()
944 {
945  // We might want to delete everything //
946  if(ClearAllEntities) {
947 
948  ClearEntities();
949 
950  ClearAllEntities = false;
951 
952  Lock lock(DeleteMutex);
953  DelayedDeleteIDS.clear();
954 
955  // All are now cleared //
956  return;
957  }
958 
959  Lock lock(DeleteMutex);
960 
961  // Return right away if no objects to delete //
962  if(DelayedDeleteIDS.empty())
963  return;
964 
965  // Search all objects and find the ones that need to be deleted //
966  for(auto iter = Entities.begin(); iter != Entities.end();) {
967 
968  // Check does id match any //
969  auto curid = *iter;
970  bool delthis = false;
971 
972  for(auto iterids = DelayedDeleteIDS.begin(); iterids != DelayedDeleteIDS.end();) {
973 
974  if(*iterids == curid) {
975  // Remove this as it will get deleted //
976  delthis = true;
977  DelayedDeleteIDS.erase(iterids);
978  break;
979 
980  } else {
981  ++iterids;
982  }
983  }
984 
985  if(delthis) {
986 
987  _DoDestroy(curid);
988  iter = Entities.erase(iter);
989 
990  // Check for end //
991  if(DelayedDeleteIDS.empty())
992  return;
993 
994  } else {
995  ++iter;
996  }
997  }
998 }
999 
1000 void GameWorld::_DoDestroy(ObjectID id)
1001 {
1002  // LOG_INFO("GameWorld destroying object " + Convert::ToString(id));
1003 
1004  if(NetworkSettings.IsAuthoritative)
1005  _ReportEntityDestruction(id);
1006 
1007  // TODO: find a better way to do this
1008  DestroyAllIn(id);
1009 
1010  // Parent destroy children //
1011  // We need to support recursively parented entities
1012  for(size_t i = 0; i < Parents.size();) {
1013 
1014  if(std::get<0>(Parents[i]) == id) {
1015 
1016  const auto childId = std::get<1>(Parents[i]);
1017 
1018  // Remove it //
1019  std::swap(Parents[i], Parents[Parents.size() - 1]);
1020  Parents.pop_back();
1021 
1022  // And then destroy //
1023  // LOG_WRITE("Destroying child ID: " + std::to_string(childId));
1024  DestroyEntity(childId);
1025 
1026  // To support recursively parented we go back to the start to scan again
1027  i = 0;
1028 
1029  } else if(std::get<1>(Parents[i]) == id) {
1030  // Child has been destroyed //
1031  // Remove it //
1032  std::swap(Parents[i], Parents[Parents.size() - 1]);
1033  Parents.pop_back();
1034 
1035  } else {
1036  ++i;
1037  }
1038  }
1039 }
1040 // ------------------------------------ //
1042 {
1043  Parents.push_back(std::make_tuple(parent, child));
1044 }
1045 // ------------------------------------ //
1047 {
1048  return std::make_tuple(nullptr, false);
1049 }
1050 
1051 DLLEXPORT std::tuple<void*, ComponentTypeInfo, bool> GameWorld::GetComponentWithType(
1052  ObjectID id, COMPONENT_TYPE type)
1053 {
1054  return std::make_tuple(nullptr, ComponentTypeInfo(-1, -1), false);
1055 }
1056 
1057 DLLEXPORT std::tuple<void*, bool> GameWorld::GetStatesFor(COMPONENT_TYPE type)
1058 {
1059  return std::make_tuple(nullptr, false);
1060 }
1061 
1063  COMPONENT_TYPE type, std::vector<std::tuple<void*, ObjectID>>& result)
1064 {
1065  return false;
1066 }
1067 
1069  const std::string& name, std::vector<std::tuple<asIScriptObject*, ObjectID>>& result)
1070 {
1071  auto iter = pimpl->RegisteredScriptComponents.find(name);
1072 
1073  if(iter == pimpl->RegisteredScriptComponents.end())
1074  return false;
1075 
1076  auto& removed = iter->second->GetRemoved();
1077 
1078  result.insert(std::end(result), std::begin(removed), std::end(removed));
1079  return true;
1080 }
1081 
1083  COMPONENT_TYPE type, std::vector<std::tuple<void*, ObjectID, ComponentTypeInfo>>& result)
1084 {
1085  return false;
1086 }
1087 
1088 DLLEXPORT bool GameWorld::GetAddedForScriptDefined(const std::string& name,
1089  std::vector<std::tuple<asIScriptObject*, ObjectID, ScriptComponentHolder*>>& result)
1090 {
1091  auto iter = pimpl->RegisteredScriptComponents.find(name);
1092 
1093  if(iter == pimpl->RegisteredScriptComponents.end())
1094  return false;
1095 
1096  auto& added = iter->second->GetAdded();
1097 
1098  result.reserve(result.size() + added.size());
1099 
1100  for(const auto& tuple : added) {
1101 
1102  result.push_back(
1103  std::make_tuple(std::get<0>(tuple), std::get<1>(tuple), iter->second.get()));
1104  }
1105 
1106  return true;
1107 }
1108 
1109 // ------------------------------------ //
1111 {
1112  // Script systems are initialized as they are created
1113 }
1114 
1116 {
1117  // This can be called after Releasing once already
1118  if(!pimpl)
1119  return;
1120 
1121  // We are responsible for script systems //
1122  for(auto iter = pimpl->RegisteredScriptSystems.begin();
1123  iter != pimpl->RegisteredScriptSystems.end(); ++iter) {
1124 
1125  iter->second->Release();
1126  }
1127 
1128  pimpl->RegisteredScriptSystems.clear();
1129 }
1130 
1132 {
1133  // We are responsible for script systems //
1134  for(auto iter = pimpl->RegisteredScriptSystems.begin();
1135  iter != pimpl->RegisteredScriptSystems.end(); ++iter) {
1136 
1137  iter->second->Suspend();
1138  }
1139 }
1140 
1142 {
1143  // We are responsible for script systems //
1144  for(auto iter = pimpl->RegisteredScriptSystems.begin();
1145  iter != pimpl->RegisteredScriptSystems.end(); ++iter) {
1146 
1147  iter->second->Resume();
1148  }
1149 }
1150 
1152 {
1153  LOG_ERROR("GameWorld: base version of _CreateSendableComponentForEntity, this shouldn't "
1154  "happen with correct configuration");
1155 }
1156 
1158 {
1159  LOG_ERROR("GameWorld: base version of _CreateReceivedComponentForEntity, this shouldn't "
1160  "happen with correct configuration");
1161 }
1162 
1163 // ------------------------------------ //
1165 {
1166  // This can be called after Releasing once already
1167  if(!pimpl)
1168  return;
1169 
1170  // Handle script types
1171  for(auto iter = pimpl->RegisteredScriptComponents.begin();
1172  iter != pimpl->RegisteredScriptComponents.end(); ++iter) {
1173 
1174  // Just try to remove like the normal c++ components until there is a better way
1175  iter->second->ReleaseComponent(id);
1176  }
1177 }
1178 // ------------------------------------ //
1179 void GameWorld::_ReportEntityDestruction(ObjectID id)
1180 {
1181  SendToAllPlayers(std::make_shared<ResponseEntityDestruction>(0, this->ID, id),
1183 }
1184 // ------------------------------------ //
1186 {
1187  // Skip if set to the same //
1188  if(frozen == WorldFrozen)
1189  return;
1190 
1191  WorldFrozen = frozen;
1192 
1193  // Send it to receiving players (if we are a server) //
1194  if(ReceivingPlayers.empty())
1195  return;
1196 
1197  // Should be safe to create the packet now and send it to all the connections //
1198  SendToAllPlayers(std::make_shared<ResponseWorldFrozen>(0, ID, WorldFrozen, TickNumber),
1200 }
1201 
1202 // DLLEXPORT RayCastHitEntity* GameWorld::CastRayGetFirstHit(const Float3& from, const Float3&
1203 // to)
1204 // {
1205 // // Create a data object for the ray cast //
1206 // RayCastData data(1, from, to);
1207 
1208 // // Call the actual ray firing function //
1209 // NewtonWorldRayCast(_PhysicalWorld->GetNewtonWorld(), &from.X, &to.X,
1210 // RayCallbackDataCallbackClosest, &data, nullptr, 0);
1211 
1212 // // Check the result //
1213 // if(data.HitEntities.size() == 0) {
1214 // // Nothing hit //
1215 // return new RayCastHitEntity();
1216 // }
1217 
1218 // // We need to increase reference count to not to accidentally delete the result while
1219 // // caller is using it
1220 // data.HitEntities[0]->AddRef();
1221 
1222 // // Return the only hit //
1223 // return data.HitEntities[0];
1224 // }
1225 
1226 
1227 // //! \todo improve this performance //
1228 // dFloat RayCallbackDataCallbackClosest(const NewtonBody* const body,
1229 // const NewtonCollision* const shapeHit, const dFloat* const hitContact,
1230 // const dFloat* const hitNormal, dLong collisionID, void* const userData,
1231 // dFloat intersectParam)
1232 // {
1233 // // Let's just store it as a NewtonBody pointer //
1234 // RayCastData* data = reinterpret_cast<RayCastData*>(userData);
1235 
1236 // if(data->HitEntities.size() == 0)
1237 // data->HitEntities.push_back(new RayCastHitEntity(body, intersectParam, data));
1238 // else
1239 // *data->HitEntities[0] = RayCastHitEntity(body, intersectParam, data);
1240 
1241 // // Continue //
1242 // return intersectParam;
1243 // }
1244 
1246 {
1247  ClearAllEntities = true;
1248 }
1249 // ------------------------------------ //
1250 void GameWorld::UpdatePlayersPositionData(ConnectedPlayer& ply)
1251 {
1252  // Get the position for this player in this world //
1253  ObjectID id = ply.GetPositionInWorld(this);
1254 
1255  // Player is using a static position at (0, 0, 0) //
1256  if(id == 0)
1257  return;
1258 
1259  try {
1260 
1261  auto& position = GetComponent<Position>(id);
1262 
1263  (void)position.Members._Position;
1264 
1265  } catch(const NotFound&) {
1266 
1267  // Player has invalid position //
1268  Logger::Get()->Warning("Player position entity has no Position component");
1269  }
1270 }
1271 // ------------------------------------ //
1273  ResponseEntityUpdate&& message, Connection& connection)
1274 {
1275  if(NetworkSettings.IsAuthoritative) {
1276 
1277  // Find matching local control before allowing
1278  auto found = ActiveLocalControl.find(message.EntityID);
1279 
1280  if(found != ActiveLocalControl.end()) {
1281 
1282  if(found->second != &connection) {
1283 
1284  // It's unsafe to dereference found->second here
1285  LOG_WARNING("GameWorld: wrong player sent local control message, entity: " +
1286  std::to_string(message.EntityID));
1287  return;
1288  }
1289 
1290  _ApplyLocalControlUpdateMessage(message.EntityID, message.TickNumber,
1291  message.UpdateData, message.ReferenceTick, -1);
1292  _OnLocalControlUpdatedEntity(message.EntityID, message.TickNumber);
1293  return;
1294  }
1295 
1296  LOG_WARNING(
1297  "GameWorld: didn't find local control entity for ResponseEntityUpdate, entity: " +
1298  std::to_string(message.EntityID));
1299  return;
1300  }
1301 
1302  // Don't apply if we don't have the entity
1303  bool found = false;
1304 
1305  for(auto entity : Entities) {
1306  if(entity == message.EntityID) {
1307 
1308  found = true;
1309  break;
1310  }
1311  }
1312 
1313  if(!found) {
1314 
1315  LOG_WARNING(
1316  "GameWorld: HandleEntityPacket: received update for non-existing entity, id: " +
1317  std::to_string(message.EntityID) +
1318  " TODO: queue for later applying in case packets are out of order");
1319  return;
1320  }
1321 
1322  // If this is controlled by us this is handled differently
1323  for(auto entity : OurActiveLocalControl) {
1324 
1325  if(entity == message.EntityID) {
1326 
1327  // TODO: apply corrections if our simulation was incorrect / not allowed
1328  return;
1329  }
1330  }
1331 
1332  try {
1333  _CreateStatesFromUpdateMessage(message.EntityID, message.TickNumber,
1334  message.UpdateData, message.ReferenceTick, -1);
1335  } catch(const InvalidArgument& e) {
1336  LOG_ERROR("GameWorld: HandleEntityPacket: trying to load update packet data caused an "
1337  "exception: ");
1338  e.PrintToLog();
1339  LOG_INFO("GameWorld: note: entity may have partially updated states, id: " +
1340  std::to_string(message.EntityID));
1341  }
1342 }
1343 
1344 DLLEXPORT ObjectID GameWorld::HandleEntityPacket(ResponseEntityCreation& message)
1345 {
1346  if(NetworkSettings.IsAuthoritative) {
1347 
1348  LOG_WARNING("GameWorld: authoritative world is ignoring ResponseEntityCreation");
1349  return NULL_OBJECT;
1350  }
1351 
1352  if(message.ComponentCount > 1000) {
1353  LOG_ERROR("GameWorld: HandleEntityPacket: entity has more than 1000 components. "
1354  "Packet is likely corrupted / forged, ignoring");
1355  return NULL_OBJECT;
1356  }
1357 
1358  // TODO: somehow detect if the ID collides with local entities (once those are allowed)
1359  Entities.push_back(message.EntityID);
1360 
1361  if(!NetworkSettings.IsAuthoritative) {
1362 
1363  if(NetworkSettings.AutoCreateNetworkComponents) {
1364  _CreateReceivedComponentForEntity(message.EntityID);
1365  }
1366  }
1367 
1368  try {
1370  message.EntityID, message.InitialComponentData, message.ComponentCount, -1);
1371 
1372  return message.EntityID;
1373  } catch(const InvalidArgument& e) {
1374  LOG_ERROR(
1375  "GameWorld: HandleEntityPacket: trying to load packet data caused an exception: ");
1376  e.PrintToLog();
1377  LOG_INFO("GameWorld: destroying invalid received entity: " +
1378  std::to_string(message.EntityID));
1379  DestroyEntity(message.EntityID);
1380 
1381  return NULL_OBJECT;
1382  }
1383 }
1384 
1385 DLLEXPORT void GameWorld::HandleEntityPacket(ResponseEntityDestruction& message)
1386 {
1387  if(NetworkSettings.IsAuthoritative) {
1388 
1389  LOG_WARNING("GameWorld: authoritative world is ignoring ResponseEntityCreation");
1390  return;
1391  }
1392 
1393  for(auto entity : Entities) {
1394  if(entity == message.EntityID) {
1395 
1396  DestroyEntity(message.EntityID);
1397  return;
1398  }
1399  }
1400 
1401  // TODO: queue if we don't have an entity with the ID
1402  LOG_WARNING("GameWorld: HandleEntityPacket: received destruction message for unknown "
1403  "entity, TODO: queue");
1404 }
1405 
1406 DLLEXPORT void GameWorld::HandleEntityPacket(ResponseEntityLocalControlStatus& message)
1407 {
1408  if(!message.Enabled) {
1409 
1410  for(auto iter = OurActiveLocalControl.begin(); iter != OurActiveLocalControl.end();
1411  ++iter) {
1412 
1413  if(*iter == message.EntityID) {
1414  OurActiveLocalControl.erase(iter);
1415  return;
1416  }
1417  }
1418 
1419  LOG_WARNING("GameWorld: received disable local control message for entity that wasn't "
1420  "controlled by us");
1421  } else {
1422 
1423  // Ignore duplicates
1424  for(auto controlled : OurActiveLocalControl)
1425  if(controlled == message.EntityID)
1426  return;
1427 
1428  OurActiveLocalControl.push_back(message.EntityID);
1429 
1430  if(NetworkSettings.AutoCreateNetworkComponents) {
1431  try {
1432  _CreateSendableComponentForEntity(message.EntityID);
1433  LOG_INFO("GameWorld: created Sendable for locally controlled entity: " +
1434  std::to_string(message.EntityID));
1435  } catch(const InvalidState&) {
1436  LOG_WARNING(
1437  "GameWorld: couldn't create Sendable for now locally controlled entity:" +
1438  std::to_string(message.EntityID));
1439  }
1440  }
1441  }
1442 }
1443 // ------------------------------------ //
1445  ObjectID id, bool enabled, const std::shared_ptr<Connection>& allowedconnection)
1446 {
1447  // Apply the change
1448  if(enabled) {
1449 
1450  // TODO: detect changing owner
1451  ActiveLocalControl[id] = allowedconnection.get();
1452  } else {
1453 
1454  auto found = ActiveLocalControl.find(id);
1455 
1456  if(found != ActiveLocalControl.end()) {
1457  ActiveLocalControl.erase(found);
1458  } else {
1459  LOG_ERROR("GameWorld: SetLocalControl: disable called on entity that wasn't being "
1460  "controlled");
1461  }
1462  }
1463 
1464  // Notify
1465  auto response = std::make_shared<ResponseEntityLocalControlStatus>(0, ID, id, enabled);
1466 
1467  allowedconnection->SendPacketToConnection(response, RECEIVE_GUARANTEE::Critical);
1468 }
1469 
1471 {
1472  Position* position = GetComponentPtr<Position>(id);
1473  Physics* physics = GetComponentPtr<Physics>(id);
1474 
1475  if(position && physics) {
1476 
1477  if(position->Marked) {
1478 
1479  physics->JumpTo(*position);
1480  }
1481  }
1482 }
1483 // ------------------------------------ //
1485 {
1486  if(!pimpl->QueuedEntityUpdates.empty()) {
1487  }
1488 
1489  // Applies packets that were received out of order. And throws out any too old packets
1490 
1491  // if(!InitialEntityPackets.empty())
1492  // _ApplyInitialEntityPackets();
1493 
1494  // if(!EntityUpdatePackets.empty())
1495  // _ApplyEntityUpdatePackets();
1496 }
1497 // ------------------------------------ //
1498 // DLLEXPORT void GameWorld::HandleClockSyncPacket(RequestWorldClockSync* data)
1499 // {
1500 // Logger::Get()->Info(
1501 // "GameWorld: adjusting our clock: Absolute: " + Convert::ToString(data->Absolute) +
1502 // ", tick: " + Convert::ToString(data->Ticks) +
1503 // ", engine ms: " + Convert::ToString(data->EngineMSTweak));
1504 
1505 // // Change our TickNumber to match //
1506 // Engine::Get()->_AdjustTickNumber(data->Ticks, data->Absolute);
1507 
1508 // if(data->EngineMSTweak)
1509 // Engine::Get()->_AdjustTickClock(data->EngineMSTweak, data->Absolute);
1510 // }
1511 // // ------------------------------------ //
1512 // DLLEXPORT void GameWorld::HandleWorldFrozenPacket(ResponseWorldFrozen* data)
1513 // {
1514 // Logger::Get()->Info(
1515 // "GameWorld(" + Convert::ToString(ID) +
1516 // "): frozen state updated, now: " + Convert::ToString<int>(data->Frozen) +
1517 // ", tick: " + Convert::ToString(data->TickNumber) +
1518 // " (our tick:" + Convert::ToString(TickNumber) + ")");
1519 
1520 // if(data->TickNumber > TickNumber) {
1521 
1522 // Logger::Get()->Write("TODO: freeze the world in the future");
1523 // }
1524 
1525 // // Set the state //
1526 // SetWorldPhysicsFrozenState(data->Frozen);
1527 
1528 // // Simulate ticks if required //
1529 // if(!data->Frozen) {
1530 
1531 // // Check how many ticks we are behind and simulate that number of physical updates
1532 // // int tickstosimulate = TickNumber - data->TickNumber;
1533 
1534 // if(tickstosimulate > 0) {
1535 
1536 // Logger::Get()->Info("GameWorld: unfreezing and simulating " +
1537 // Convert::ToString(tickstosimulate * TICKSPEED) +
1538 // " ms worth of more physical updates");
1539 
1540 // DEBUG_BREAK;
1541 // // _PhysicalWorld->AdjustClock(tickstosimulate * TICKSPEED);
1542 // }
1543 
1544 
1545 // } else {
1546 
1547 // // Snap objects back //
1548 // Logger::Get()->Info("TODO: world freeze snap things back a bit");
1549 // }
1550 // }
1551 // ------------------------------------ //
1552 DLLEXPORT CScriptArray* GameWorld::GetRemovedIDsForComponents(CScriptArray* componenttypes)
1553 {
1554  if(!componenttypes)
1555  return nullptr;
1556 
1557  if(componenttypes->GetElementTypeId() !=
1559 
1560  LOG_ERROR("GameWorld: GetRemovedIDsForComponents: given an array of wrong type");
1561  componenttypes->Release();
1562  return nullptr;
1563  }
1564 
1565  std::vector<std::tuple<void*, ObjectID>> result;
1566 
1567  for(asUINT i = 0; i < componenttypes->GetSize(); ++i) {
1568 
1569  uint16_t* type = static_cast<uint16_t*>(componenttypes->At(i));
1570 
1571  if(!GetRemovedFor(static_cast<COMPONENT_TYPE>(*type), result)) {
1572 
1573  LOG_WARNING("GameWorld: GetRemovedIDsForComponents: unknown component type: " +
1574  std::to_string(*type));
1575  }
1576  }
1577 
1578  componenttypes->Release();
1579 
1580  asITypeInfo* typeInfo =
1581  ScriptExecutor::Get()->GetASEngine()->GetTypeInfoByDecl("array<ObjectID>");
1582 
1583  CScriptArray* array = CScriptArray::Create(typeInfo, static_cast<asUINT>(result.size()));
1584 
1585  if(!array)
1586  return nullptr;
1587 
1588  for(asUINT i = 0; i < static_cast<asUINT>(result.size()); ++i) {
1589 
1590  array->SetValue(i, &std::get<1>(result[i]));
1591  }
1592 
1593  return array;
1594 }
1595 
1596 DLLEXPORT CScriptArray* GameWorld::GetRemovedIDsForScriptComponents(CScriptArray* typenames)
1597 {
1598  if(!typenames)
1599  return nullptr;
1600 
1601  if(typenames->GetElementTypeId() !=
1603 
1604  LOG_ERROR("GameWorld: GetRemovedIDsForScriptComponents: given an array of wrong type "
1605  "(expected array<string>)");
1606  typenames->Release();
1607  return nullptr;
1608  }
1609 
1610  std::vector<std::tuple<asIScriptObject*, ObjectID>> result;
1611 
1612  for(asUINT i = 0; i < typenames->GetSize(); ++i) {
1613 
1614  std::string* type = static_cast<std::string*>(typenames->At(i));
1615 
1616  if(!GetRemovedForScriptDefined(*type, result)) {
1617 
1618  LOG_WARNING(
1619  "GameWorld: GetRemovedIDsForScriptComponents: unknown component type: " +
1620  *type);
1621  }
1622  }
1623 
1624  typenames->Release();
1625 
1626  asITypeInfo* typeInfo =
1627  ScriptExecutor::Get()->GetASEngine()->GetTypeInfoByDecl("array<ObjectID>");
1628 
1629  CScriptArray* array = CScriptArray::Create(typeInfo, static_cast<asUINT>(result.size()));
1630 
1631  if(!array)
1632  return nullptr;
1633 
1634  for(asUINT i = 0; i < static_cast<asUINT>(result.size()); ++i) {
1635 
1636  array->SetValue(i, &std::get<1>(result[i]));
1637  }
1638 
1639  return array;
1640 }
1641 
1643  const std::string& name, asIScriptFunction* factory)
1644 {
1645  // Skip if already registered //
1646  if(pimpl->RegisteredScriptComponents.find(name) !=
1647  pimpl->RegisteredScriptComponents.end()) {
1648  factory->Release();
1649  return false;
1650  }
1651 
1652  // ScriptComponentHolder takes care of releasing the reference
1653  pimpl->RegisteredScriptComponents[name] =
1654  ScriptComponentHolder::MakeShared<ScriptComponentHolder>(name, factory, this);
1655 
1656  return true;
1657 }
1658 
1660 {
1661  // if called after release
1662  if(!pimpl)
1663  return nullptr;
1664 
1665  auto iter = pimpl->RegisteredScriptComponents.find(name);
1666 
1667  if(iter == pimpl->RegisteredScriptComponents.end())
1668  return nullptr;
1669 
1670  iter->second->AddRef();
1671  return iter->second.get();
1672 }
1673 
1675  const std::string& name, asIScriptObject* system)
1676 {
1677  if(!system) {
1678  LOG_ERROR("GameWorld: RegisterScriptSystem: passed null object as new system");
1679  return false;
1680  }
1681 
1682  // if called after release
1683  if(!pimpl) {
1684  system->Release();
1685  return false;
1686  }
1687 
1688  // Skip if already registered //
1689  if(pimpl->RegisteredScriptSystems.find(name) != pimpl->RegisteredScriptSystems.end()) {
1690  system->Release();
1691  return false;
1692  }
1693 
1694  // Create a wrapper for it //
1695  // The wrapper handles unreferencing the handle
1696  pimpl->RegisteredScriptSystems[name] = std::make_unique<ScriptSystemWrapper>(name, system);
1697 
1698  // Might as well call Init now as other systems are almost certainly initialized as well //
1699  pimpl->RegisteredScriptSystems[name]->Init(this);
1700  return true;
1701 }
1702 
1703 DLLEXPORT bool GameWorld::UnregisterScriptSystem(const std::string& name)
1704 {
1705  // if called after release
1706  if(!pimpl) {
1707  return false;
1708  }
1709 
1710  const auto iter = pimpl->RegisteredScriptSystems.find(name);
1711  if(iter == pimpl->RegisteredScriptSystems.end()) {
1712  return false;
1713  }
1714 
1715  iter->second->Release();
1716  pimpl->RegisteredScriptSystems.erase(iter);
1717 
1718  return true;
1719 }
1720 
1721 DLLEXPORT asIScriptObject* GameWorld::GetScriptSystem(const std::string& name)
1722 {
1723  // if called after release
1724  if(!pimpl)
1725  return nullptr;
1726 
1727  auto iter = pimpl->RegisteredScriptSystems.find(name);
1728 
1729  if(iter == pimpl->RegisteredScriptSystems.end()) {
1730 
1731  LOG_ERROR("GameWorld: GetScriptSystemDirect: world has no system called: " + name);
1732  return nullptr;
1733  }
1734 
1735  return iter->second->GetASImplementationObject();
1736 }
1737 // ------------------------------------ //
1739 {
1740  if(!LinkedToWindow) {
1741  LOG_WARNING(
1742  "GameWorld: unlink from window called while this wasn't linked to a window");
1743  return;
1744  }
1745 
1746  if(window != LinkedToWindow) {
1747 
1748  throw InvalidArgument("GameWorld attempted to be unlinked from window that wasn't the "
1749  "one it was linked to");
1750  }
1751 
1752  pimpl->WorldCamera->getViewport()->setTarget(nullptr);
1753 
1754  // ogre->getCompositorManager2()->removeWorkspace(WorldWorkspace);
1755  // WorldWorkspace = nullptr;
1756  LinkedToWindow = nullptr;
1757 
1758  if(!TickWhileInBackground) {
1760  }
1761 
1762  InBackground = true;
1763 }
1764 
1766 {
1767  // LEVIATHAN_ASSERT(WorldsScene, "World is not initialized");
1768 
1769  if(!window)
1770  throw InvalidArgument("GameWorld attempted to be linked to a nullptr window");
1771 
1772  if(LinkedToWindow // || WorldWorkspace
1773  ) {
1774 
1775  throw InvalidArgument(
1776  "GameWorld attempted to be linked to a window while it is already linked");
1777  }
1778 
1779  LinkedToWindow = window;
1780 
1781  // // Create the workspace for this scene //
1782  // // Which will be rendered before the overlay workspace but after potential
1783  // // clearing workspace
1784  // WorldWorkspace = ogre->getCompositorManager2()->addWorkspace(WorldsScene,
1785  // LinkedToWindow->GetOgreWindow(), WorldSceneCamera, "WorldsWorkspace", true, 0);
1786  pimpl->WorldCamera->getViewport()->setTarget(window->GetBSFWindow());
1787 
1788  // TODO: this needs to be reapplied every time the window is resized
1789  // TODO: ask for BSF auto aspect ratio (setAutoAspectRatio?)
1790  int32_t width, height;
1791  window->GetSize(width, height);
1792  pimpl->WorldCamera->setAspectRatio(width / static_cast<float>(height));
1793 
1794  if(!TickWhileInBackground) {
1795  _DoResumeSystems();
1796  }
1797 
1798  InBackground = false;
1799 }
1800 
1801 DLLEXPORT void GameWorld::SetRunInBackground(bool tickinbackground)
1802 {
1803  TickWhileInBackground = tickinbackground;
1804 }
1805 
1806 // // ------------------ RayCastHitEntity ------------------ //
1807 // DLLEXPORT Leviathan::RayCastHitEntity::RayCastHitEntity(
1808 // const NewtonBody* ptr /*= nullptr*/, const float& tvar, RayCastData* ownerptr) :
1809 // HitEntity(ptr),
1810 // HitVariable(tvar)
1811 // {
1812 // if(ownerptr) {
1813 // HitLocation = ownerptr->BaseHitLocationCalcVar * HitVariable;
1814 // } else {
1815 // HitLocation = (Float3)0;
1816 // }
1817 // }
1818 
1819 // DLLEXPORT bool Leviathan::RayCastHitEntity::HasHit()
1820 // {
1821 // return HitEntity != nullptr;
1822 // }
1823 
1824 // DLLEXPORT bool Leviathan::RayCastHitEntity::DoesBodyMatchThisHit(NewtonBody* other)
1825 // {
1826 // return HitEntity == other;
1827 // }
1828 
1829 // DLLEXPORT Float3 Leviathan::RayCastHitEntity::GetPosition()
1830 // {
1831 // return HitLocation;
1832 // }
1833 
1834 // DLLEXPORT RayCastHitEntity& Leviathan::RayCastHitEntity::operator=(
1835 // const RayCastHitEntity& other)
1836 // {
1837 // HitEntity = other.HitEntity;
1838 // HitVariable = other.HitVariable;
1839 // HitLocation = other.HitLocation;
1840 
1841 // return *this;
1842 // }
1843 // // ------------------ RayCastData ------------------ //
1844 // DLLEXPORT Leviathan::RayCastData::RayCastData(
1845 // int maxcount, const Float3& from, const Float3& to) :
1846 // MaxCount(maxcount),
1847 // // Formula based on helpful guy on Newton forums
1848 // BaseHitLocationCalcVar(from + (to - from))
1849 // {
1850 // // Reserve memory for maximum number of objects //
1851 // HitEntities.reserve(maxcount);
1852 // }
1853 
1854 // DLLEXPORT Leviathan::RayCastData::~RayCastData()
1855 // {
1856 // // We want to release all hit data //
1857 // SAFE_RELEASE_VECTOR(HitEntities);
1858 // }
DLLEXPORT bs::HSceneObject GetCameraSceneObject()
Returns the scene object the camera is attached to.
Definition: GameWorld.cpp:474
virtual DLLEXPORT std::tuple< void *, ComponentTypeInfo, bool > GetComponentWithType(ObjectID id, COMPONENT_TYPE type)
Gets a component of type or returns nullptr.
Definition: GameWorld.cpp:1051
DLLEXPORT asIScriptObject * GetScriptSystem(const std::string &name)
Returns the underlying angelscript object that implements a script system.
Definition: GameWorld.cpp:1721
static bool SunCreated
Definition: GameWorld.cpp:269
DLLEXPORT void Write(const std::string &data) override
Definition: Logger.cpp:113
virtual DLLEXPORT void _DoSuspendSystems()
Called when this is put in the background and systems (the sound system) should suspend their active ...
Definition: GameWorld.cpp:1131
DLLEXPORT std::shared_ptr< SentRequest > SendPacketToConnection(const std::shared_ptr< NetworkRequest > &request, RECEIVE_GUARANTEE guarantee)
Definition: Connection.cpp:139
DLLEXPORT ObjectID CreateEntity()
Creates a new empty entity and returns its id.
Definition: GameWorld.cpp:815
DLLEXPORT void GetSize(int32_t &width, int32_t &height) const
Definition: Window.cpp:896
DLLEXPORT asIScriptEngine * GetASEngine()
#define LOG_INFO(x)
Definition: Define.h:88
constexpr ObjectID NULL_OBJECT
Definition: EntityCommon.h:14
int32_t ObjectID
Definition: EntityCommon.h:11
DLLEXPORT bool RegisterScriptComponentType(const std::string &name, asIScriptFunction *factory)
Registers a component type from scripts.
Definition: GameWorld.cpp:1642
DLLEXPORT void Info(const std::string &data) override
Definition: Logger.cpp:164
#define LOG_ERROR(x)
Definition: Define.h:90
DLLEXPORT void SetEntitysParent(ObjectID child, ObjectID parent)
Makes child entity be deleted when parent is deleted.
Definition: GameWorld.cpp:1041
virtual DLLEXPORT void OnLinkToWindow(Window *window, Graphics *graphics)
Called when this is added to a Window.
Definition: GameWorld.cpp:1765
static int Get(ScriptExecutor *resolver)
DLLEXPORT const auto & GetBSFWindow() const
Definition: Window.h:166
DLLEXPORT void HandleEntityPacket(ResponseEntityUpdate &&message, Connection &connection)
Applies an entity update packet.
Definition: GameWorld.cpp:1272
#define LOG_FATAL(x)
Definition: Define.h:92
COMPONENT_TYPE
Must contain all valid Component types.
Definition: Component.h:21
virtual DLLEXPORT void _DoSystemsRelease()
Called in Release when systems should run their shutdown logic.
Definition: GameWorld.cpp:1115
virtual DLLEXPORT void DestroyAllIn(ObjectID id)
Removes all components from an entity.
Definition: GameWorld.cpp:1164
RECEIVE_GUARANTEE
Controls whether a packet is critical.
Definition: CommonNetwork.h:49
virtual DLLEXPORT void _CreateSendableComponentForEntity(ObjectID id)
Called when this base class wants to create a Sendable component.
Definition: GameWorld.cpp:1151
virtual DLLEXPORT void _ApplyLocalControlUpdateMessage(ObjectID id, int32_t ticknumber, sf::Packet &data, int32_t referencetick, int decodedtype)
Called to apply local control from a clients message.
Definition: GameWorld.cpp:640
DLLEXPORT void SetLightProperties(const Float3 &colour, float intensity=0.0001f, const Float3 &direction=Float3(0.55f, -0.3f, 0.75f), float sourceradius=0.5f, bool castsshadows=true)
Sets the sunlight properties.
Definition: GameWorld.cpp:338
DLLEXPORT CScriptArray * GetRemovedIDsForComponents(CScriptArray *componenttypes)
Returns a list of ObjectIDs that have been removed from any of the specified component types.
Definition: GameWorld.cpp:1552
DLLEXPORT void SetLocalControl(ObjectID id, bool enabled, const std::shared_ptr< Connection > &allowedconnection)
Sets local control on a client over an entity or disables it.
Definition: GameWorld.cpp:1444
static int GetID()
Definition: IDFactory.h:17
std::map< std::string, ScriptComponentHolder::pointer > RegisteredScriptComponents
Definition: GameWorld.cpp:62
constexpr auto TICKSPEED
Number of milliseconds between engine and world ticks.
Definition: Define.h:22
virtual DLLEXPORT void _CreateReceivedComponentForEntity(ObjectID id)
Called when this base class wants to create a Received component.
Definition: GameWorld.cpp:1157
DLLEXPORT bs::HTexture LoadTextureByName(const std::string &name)
Works the same as LoadShaderByName.
Definition: Graphics.cpp:581
virtual DLLEXPORT bool GetAddedFor(COMPONENT_TYPE type, std::vector< std::tuple< void *, ObjectID, ComponentTypeInfo >> &result)
Gets a list of created components of type.
Definition: GameWorld.cpp:1082
Base class for all exceptions thrown by Leviathan.
Definition: Exceptions.h:10
DLLEXPORT void QueueDestroyEntity(ObjectID id)
Deletes an entity during the next tick.
Definition: GameWorld.cpp:911
virtual DLLEXPORT void HandleAddedAndDeleted()
Handles added entities and components.
Definition: GameWorld.cpp:722
DLLEXPORT bool IsConnectionInWorld(Connection &connection) const
Returns true if a player with the given connection is receiving updates for this world.
Definition: GameWorld.cpp:485
const std::shared_ptr< Connection > & GetConnection()
#define LOG_WARNING(x)
Definition: Define.h:89
DLLEXPORT bool GetRemovedForScriptDefined(const std::string &name, std::vector< std::tuple< asIScriptObject *, ObjectID >> &result)
Variant of GetRemovedFor for script defined types.
Definition: GameWorld.cpp:1068
DLLEXPORT void ApplyQueuedPackets()
Applies packets that were received out of order. And throws out any too old packets.
Definition: GameWorld.cpp:1484
virtual DLLEXPORT void _ResetOrReleaseComponents()
Resets components in holders. Used together with _ResetSystems.
Definition: GameWorld.cpp:757
virtual DLLEXPORT void OnUnLinkedFromWindow(Window *window, Graphics *graphics)
Used to detect that this world is in the background and should not tick.
Definition: GameWorld.cpp:1738
static std::tuple< bool, StateT > Interpolate(const StateHolder< StateT > &stateholder, ObjectID entity, ComponentT *entitycomponent, int currenttick, int timeintick)
Interpolates states for component.
static DLLEXPORT ScriptExecutor * Get()
bool IsAuthoritative
This is true on the server.
const std::string & GetNickname() override
Returns a friendly name, nickname used to represent this entity.
DLLEXPORT void SetSunlight()
Definition: GameWorld.cpp:271
DLLEXPORT const auto & GetNetworkSettings() const
Definition: GameWorld.h:346
DLLEXPORT void SetWorldPhysicsFrozenState(bool frozen)
Definition: GameWorld.cpp:1185
TComponent & GetComponent(ObjectID id)
Definition: GameWorld.h:205
void swap(Value &a, Value &b)
Definition: json.h:1359
virtual DLLEXPORT void _CreateStatesFromUpdateMessage(ObjectID id, int32_t ticknumber, sf::Packet &data, int32_t referencetick, int decodedtype)
Called to deserialize entity component states from a packet.
Definition: GameWorld.cpp:632
DLLEXPORT NamedVars * GetValues()
Definition: AppDefine.cpp:40
static int LayerNumber
A temporary solution around no multiple scenes in BSF.
Definition: GameWorld.cpp:79
DLLEXPORT CScriptArray * GetRemovedIDsForScriptComponents(CScriptArray *typenames)
Returns a list of ObjectIDs that have been removed from any of the script registered component types ...
Definition: GameWorld.cpp:1596
DLLEXPORT void Warning(const std::string &data) override
Definition: Logger.cpp:190
Entity has position and direction it is looking at.
Definition: Components.h:34
#define LOG_WRITE(x)
Definition: Define.h:91
DLLEXPORT ~GameWorld()
Definition: GameWorld.cpp:92
SoundDevice * GetSoundDevice()
Definition: Engine.h:221
std::vector< std::tuple< WantedClockType::time_point, ResponseEntityUpdate > > QueuedEntityUpdates
Queued entity updates. Contains the time it was received in order to throw out old ones.
Definition: GameWorld.cpp:67
DLLEXPORT bool Init(const WorldNetworkSettings &network, Graphics *graphics)
Creates resources for the world to work.
Definition: GameWorld.cpp:103
Graphics * GetGraphics()
Definition: Engine.h:179
DLLEXPORT float GetTickProgress() const
Returns float between 0.f and 1.f based on how far current tick has progressed.
Definition: GameWorld.cpp:790
DLLEXPORT bool ShouldPlayerReceiveEntity(Position &atposition, Connection &connection)
Returns true when the player matching the connection should receive updates about an entity.
Definition: GameWorld.cpp:479
unsigned short uint16_t
Definition: core.h:39
#define LEVIATHAN_ASSERT(x, msg)
Definition: Define.h:98
bs::Scene * GetScene()
Definition: GameWorld.h:315
DLLEXPORT bool RegisterScriptSystem(const std::string &name, asIScriptObject *system)
Registers a new system defined in a script. Must implement the ScriptSystem interface.
Definition: GameWorld.cpp:1674
static std::string ToString(const T &val)
Definition: Convert.h:72
virtual DLLEXPORT void ClearAddedAndRemoved()
Clears the added components. Call after HandleAddedAndDeleted.
Definition: GameWorld.cpp:732
DLLEXPORT GameWorld(int32_t worldtype, const std::shared_ptr< PhysicsMaterialManager > &physicsMaterials, int worldid=-1)
Definition: GameWorld.cpp:85
Entity has a physical component.
Definition: Components.h:346
StateHolder< typename TComponent::StateT > & GetStatesFor()
Definition: GameWorld.h:253
virtual DLLEXPORT void CaptureEntityState(ObjectID id, EntityState &curstate) const
Captures the current state of an entity.
Definition: GameWorld.cpp:615
DLLEXPORT void RemoveSunlight()
Definition: GameWorld.cpp:295
DLLEXPORT int64_t GetTimeSinceLastTick() const
Calculates how long has elapsed since the last tick.
Definition: Engine.cpp:1266
static DLLEXPORT Logger * Get()
Definition: Logger.cpp:106
DLLEXPORT void SetSoundListenerPosition(const Float3 &pos, const Float4 &orientation)
Loads the file and plays the sound.
virtual DLLEXPORT void _OnLocalControlUpdatedEntity(ObjectID id, int32_t ticknumber)
This method is for doing checks after applying client sent state to the server.
Definition: GameWorld.cpp:1470
AppDef * GetDefinition()
Definition: Engine.h:205
DLLEXPORT std::tuple< int, int > GetTickAndTime() const
Returns a tuple of the current tick number and how long it has passed since last tick.
Definition: GameWorld.cpp:800
DLLEXPORT void Release()
Release resources.
Definition: GameWorld.cpp:130
A class that represents a human player.
virtual DLLEXPORT void RunFrameRenderSystems(int tick, int timeintick)
Called by Render which is called from a Window if this is linked to one.
Definition: GameWorld.cpp:771
DLLEXPORT ObjectID GetPositionInWorld(GameWorld *world) const
Returns the object that contains this players position in a certain world or 0.
DLLEXPORT void Render(int mspassed, int tick, int timeintick)
Runs systems required for a rendering run. Also updates camera positions.
Definition: GameWorld.cpp:369
virtual DLLEXPORT void _CreateComponentsFromCreationMessage(ObjectID id, sf::Packet &data, int entriesleft, int decodedtype)
Called to deserialize initial entity components and their static state.
Definition: GameWorld.cpp:622
DLLEXPORT ScriptComponentHolder * GetScriptComponentHolder(const std::string &name)
Retrieves a script registered component type holder.
Definition: GameWorld.cpp:1659
DLLEXPORT void JumpTo(Position &target)
Syncs this physics body to a changed position.
Definition: Components.cpp:117
Properties that a camera entity has (will also need a Position component)
Definition: Components.h:713
uint16_t FOV
Horizontal (ie. "normal") field of view.
Definition: Components.h:724
Class that handles a single connection to another instance.
Definition: Connection.h:105
DLLEXPORT void Tick(int currenttick)
Used to keep track of passed ticks and trigger timed triggers.
Definition: GameWorld.cpp:647
#define DLLEXPORT
Definition: Include.h:84
DLLEXPORT void SetCamera(ObjectID object)
Sets the entity that acts as a camera.
Definition: GameWorld.cpp:435
DLLEXPORT void MarkForClear()
Marks all entities to be deleted.
Definition: GameWorld.cpp:1245
virtual DLLEXPORT void _DoResumeSystems()
Opposite of _DoSuspendSystems.
Definition: GameWorld.cpp:1141
static DLLEXPORT Engine * Get()
Definition: Engine.cpp:85
virtual DLLEXPORT void _RunTickSystems()
Called by Tick.
Definition: GameWorld.cpp:780
DLLEXPORT bool GetAddedForScriptDefined(const std::string &name, std::vector< std::tuple< asIScriptObject *, ObjectID, ScriptComponentHolder * >> &result)
Variant of GetAddedFor for script defined types.
Definition: GameWorld.cpp:1088
virtual DLLEXPORT uint32_t CaptureEntityStaticState(ObjectID id, sf::Packet &receiver) const
Captures the initial parameters of an entity with components that don't have current state synchroniz...
Definition: GameWorld.cpp:617
DLLEXPORT void ClearEntities()
Clears all objects from the world.
Definition: GameWorld.cpp:842
The access mask controls which registered functions and classes a script sees.
Definition: GameModule.h:12
Holder of state for a whole entity.
unsigned int uint32_t
Definition: core.h:40
virtual DLLEXPORT void SetRunInBackground(bool tickinbackground)
Configures this world to run tick even when not attached to a window.
Definition: GameWorld.cpp:1801
DLLEXPORT void Error(const std::string &data) override
Definition: Logger.cpp:177
DLLEXPORT void SetSkybox(const std::string &skyboxname, float brightness=1.f)
Definition: GameWorld.cpp:305
virtual DLLEXPORT bool GetRemovedFor(COMPONENT_TYPE type, std::vector< std::tuple< void *, ObjectID >> &result)
Gets a list of destroyed components of type.
Definition: GameWorld.cpp:1062
DLLEXPORT void SetPlayerReceiveWorld(std::shared_ptr< ConnectedPlayer > ply)
Verifies that player is receiving this world.
Definition: GameWorld.cpp:498
virtual DLLEXPORT void _DoSystemsInit()
Called in Init when systems should run their initialization logic.
Definition: GameWorld.cpp:1110
virtual DLLEXPORT void _ResetSystems()
Resets stored nodes in systems. Used together with _ResetComponents.
Definition: GameWorld.cpp:743
bool GraphicalMode
If false a graphical Ogre window hasn't been created and purely graphical stuff should be skipped.
Definition: GameWorld.h:616
DLLEXPORT bool UnregisterScriptSystem(const std::string &name)
Unregisters a script system that was registered with RegisterScriptSystem.
Definition: GameWorld.cpp:1703
std::unique_lock< std::mutex > Lock
Definition: ThreadSafe.h:18
DLLEXPORT void DestroyEntity(ObjectID id)
Destroys an entity and all of its components.
Definition: GameWorld.cpp:887
virtual DLLEXPORT void PrintToLog() const noexcept
Definition: Exceptions.cpp:35
DLLEXPORT bs::Ray CastRayFromCamera(int x, int y) const
Casts a ray from the active camera.
Definition: GameWorld.cpp:460
DLLEXPORT void SendToAllPlayers(const std::shared_ptr< NetworkResponse > &response, RECEIVE_GUARANTEE guarantee) const
Sends a packet to all connected players.
Definition: GameWorld.cpp:598
std::map< std::string, std::unique_ptr< ScriptSystemWrapper > > RegisteredScriptSystems
Definition: GameWorld.cpp:63
DLLEXPORT int GetPhysicalMaterial(const std::string &name)
Fetches the physical material ID from the material manager.
Definition: GameWorld.cpp:879