TDME2  1.9.200
ApplicationClient.cpp
Go to the documentation of this file.
2 
3 #include <algorithm>
4 #include <memory>
5 #include <string>
6 #include <vector>
7 
8 #include <tdme/tdme.h>
9 #include <tdme/audio/Audio.h>
10 #include <tdme/audio/AudioEntity.h>
16 #include <tdme/engine/Camera.h>
17 #include <tdme/engine/Entity.h>
24 #include <tdme/utilities/Console.h>
25 #include <tdme/utilities/Time.h>
26 
27 using std::make_unique;
28 using std::sort;
29 using std::string;
30 using std::to_string;
31 using std::unique_ptr;
32 using std::vector;
33 
34 using tdme::audio::Audio;
52 
53 ApplicationClient::ApplicationClient(Context* context, UDPClient* udpClient) : Thread("applicationserverclientthread"), mutex("applicationserverclientthread-mutex") {
54  this->context = unique_ptr<Context>(context);
55  this->udpClient = unique_ptr<UDPClient>(udpClient);
56  this->context->setLogicsMutex(getMutex());
57 }
58 
60  if (udpClient != nullptr) {
61  // stop udp client
62  udpClient->stop();
63  udpClient->join();
64  }
65  //
66  context->shutdown();
67 }
68 
70  Console::println("ApplicationClient::run(): init");
71 
72  //
73  auto timeLast = Time::getCurrentMillis();
74  vector<LogicNetworkPacket> inLogicsNetworkPacketsUnhandled;
75  vector<LogicNetworkPacket> safeLogicNetworkPackets;
76  vector<LogicNetworkPacket> fastLogicNetworkPackets;
77  vector<LogicNetworkPacket> inNetworkPackets;
78  while(isStopRequested() == false) {
79  auto timeNow = Time::getCurrentMillis();
80 
81  // push unhandlet packets to IN packets, display an warning if not handled for 1s
82  for (auto& logicNetworkPacket: inLogicsNetworkPacketsUnhandled) {
83  if (timeNow - logicNetworkPacket.getTime() > 1000L) {
84  string logicTypeIdString;
85  logicTypeIdString+= (char)((logicNetworkPacket.getLogicTypeId() >> 0) & 0xFF);
86  logicTypeIdString+= (char)((logicNetworkPacket.getLogicTypeId() >> 8) & 0xFF);
87  logicTypeIdString+= (char)((logicNetworkPacket.getLogicTypeId() >> 16) & 0xFF);
88  logicTypeIdString+= (char)((logicNetworkPacket.getLogicTypeId() >> 24) & 0xFF);
89  Console::println("ApplicationClient::run(): unhandled IN packet: 1s late: " + logicTypeIdString);
90  Console::println("Packet ASCII: ");
91  for (auto i = 0; i < 255; i++) {
92  Console::print(string() + (char)(logicNetworkPacket.getData()[i]));
93  }
94  Console::println("Packet ORD: ");
95  for (auto i = 0; i < 255; i++) {
96  Console::print(to_string(int(logicNetworkPacket.getData()[i])) + " ");
97  }
98  Console::println();
99  }
100  logicNetworkPacket.setReinjected();
101  inNetworkPackets.push_back(logicNetworkPacket);
102  }
103  inLogicsNetworkPacketsUnhandled.clear();
104 
105  // get in packets
106  mutex.lock();
107  if (udpClient != nullptr) {
108  while (true == true) {
109  //
110  unique_ptr<UDPClientMessage> message(udpClient->receiveMessage());
111  //
112  if (message == nullptr) break;
113  // get if safe message
114  auto packet = message->getPacket();
115  if (packet == nullptr) {
116  continue;
117  }
118  // safe
119  auto safe = packet->getBool();
120  if (safe == true && udpClient->processSafeMessage(message.get()) == false) {
121  continue;
122  }
123  // Console::println("got packet safe: " + to_string(safe == 1));
124  while (packet->getPosition() < UDPPacket::PACKET_MAX_SIZE - 17) {
125  // size
126  auto size = packet->getByte();
127  // Console::println("got packet size " + to_string((int)size));
128  // end of
129  if (size == 0) break;
130  // message logic type id
131  auto logicTypeId = packet->getInt();
132  // Console::println(string("got packet logic ") + (char)logicTypeId);
133  // create network packet
134  inNetworkPackets.emplace_back(
135  message->getMessageId(),
136  safe,
137  message->getRetryCount(),
138  logicTypeId,
139  packet,
140  size
141  );
142  }
143  }
144  }
145 
146  //
147  context->initUpdateLogics();
148 
149  // handle in packets
150  handleInNetworkPackets(context->getLogics(), inNetworkPackets);
151 
152  // after handling network packets its possible that logics have been added, handle in packets for those too
153  while (true == true) {
154  // get new logics
155  auto newLogics = context->getNewLogics();
156 
157  // basically add new logics to logics
158  auto addedLogics = context->addNewLogics();
159  if (addedLogics == 0) break;
160 
161  // handle in network packets for new logics
162  handleInNetworkPackets(newLogics, inNetworkPackets);
163  }
164 
165  // update current logics
166  for (auto logic: context->getLogics()) {
167  logic->updateLogic();
168  }
169 
170  // handle in packets and do logics for new logics
171  while (true == true) {
172  // get new logics
173  auto newLogics = context->getNewLogics();
174 
175  // add new logics to logics
176  auto addedLogics = context->addNewLogics();
177  if (addedLogics == 0) break;
178 
179  // run logics new
180  handleInNetworkPackets(newLogics, inNetworkPackets);
181 
182  // run new logics
183  for (auto newLogic: newLogics) newLogic->updateLogic();
184  }
185 
186  // fire on logics processed for game logics
187  for (auto logic: context->getLogics()) {
188  logic->onLogicsProcessed();
189  }
190 
191  //
192  context->doneUpdateLogics();
193 
194  // check if there are in packets that have not yet been processed
195  for (auto& packet: inNetworkPackets) {
196  if (packet.isProcessed() == false) {
197  inLogicsNetworkPacketsUnhandled.push_back(packet);
198  }
199  }
200  //
201  inNetworkPackets.clear();
202 
203  // network sending
204  if (udpClient == nullptr) {
205  // done
206  mutex.unlock();
207 
208  // We have no UDP client, so just clear outgoing packages
209  for (auto logic: context->getLogics()) {
210  NetworkLogic* networkLogic = dynamic_cast<NetworkLogic*>(logic);
211  if (networkLogic != nullptr) {
212  networkLogic->getNetworkPackets().clear();
213  }
214  }
215  } else {
216  // We have a UDP client, so send packages
217  for (auto logic: context->getLogics()) {
218  NetworkLogic* networkLogic = dynamic_cast<NetworkLogic*>(logic);
219  if (networkLogic != nullptr) {
220  for (auto& logicNetworkPacket: networkLogic->getNetworkPackets()) {
221  if (logicNetworkPacket.getLogicTypeId() == LogicNetworkPacket::LOGIC_TYPEID_NONE) {
222  logicNetworkPacket.setLogicTypeId(networkLogic->getNetworkPacketTypeId());
223  }
224  if (logicNetworkPacket.getSafe() == true) {
225  safeLogicNetworkPackets.push_back(logicNetworkPacket);
226  } else {
227  fastLogicNetworkPackets.push_back(logicNetworkPacket);
228  }
229  }
230  networkLogic->getNetworkPackets().clear();
231  }
232  }
233  // done
234  mutex.unlock();
235 
236  // send safe messages
237  {
238  auto udpClientPacket = make_unique<UDPPacket>();
239  // safe
240  udpClientPacket->putBool(true);
241  //
242  for (auto& safeNetworkPacket: safeLogicNetworkPackets) {
243  // size
244  auto size = safeNetworkPacket.getPosition();
245  // datagram size
246  auto datagramSize = udpClientPacket->getSize();
247  //
248  if (datagramSize + 2 + (uint16_t)size > UDPPacket::PACKET_MAX_SIZE - 17) {
249  if (datagramSize < UDPPacket::PACKET_MAX_SIZE - 17) {
250  // no more network packets, size = 0
251  udpClientPacket->putByte(0);
252  }
253  udpClient->sendMessage(udpClient->createMessage(udpClientPacket.release()), true);
254  udpClientPacket = make_unique<UDPPacket>();
255  // safe
256  udpClientPacket->putBool(true);
257  }
258  // size
259  udpClientPacket->putByte(size);
260  // logic type id
261  udpClientPacket->putInt(safeNetworkPacket.getLogicTypeId());
262  // payload
263  udpClientPacket->putBytes(safeNetworkPacket.getData(), safeNetworkPacket.getPosition());
264  }
265  // datagram size
266  auto datagramSize = udpClientPacket->getSize();
267  if (datagramSize > 1) {
268  if (datagramSize < UDPPacket::PACKET_MAX_SIZE - 17) {
269  // no more network packets, size = 0
270  udpClientPacket->putByte(0);
271  }
272  udpClient->sendMessage(udpClient->createMessage(udpClientPacket.release()), true);
273  }
274  //
275  safeLogicNetworkPackets.clear();
276  }
277  // send fast messages
278  {
279  auto udpClientPacket = make_unique<UDPPacket>();
280  // safe
281  udpClientPacket->putBool(false);
282  //
283  for (auto& fastNetworkPacket: fastLogicNetworkPackets) {
284  // size
285  auto size = fastNetworkPacket.getPosition();
286  // datagram size
287  auto datagramSize = udpClientPacket->getSize();
288  if (datagramSize + 2 + (uint16_t)size > UDPPacket::PACKET_MAX_SIZE - 17) {
289  if (datagramSize < UDPPacket::PACKET_MAX_SIZE - 17) {
290  // no more network packets, size = 0
291  udpClientPacket->putByte(0);
292  }
293  udpClient->sendMessage(udpClient->createMessage(udpClientPacket.release()), true);
294  udpClientPacket = make_unique<UDPPacket>();
295  // safe
296  udpClientPacket->putBool(false);
297  }
298  // size
299  udpClientPacket->putByte(size);
300  // gmae logic type id
301  udpClientPacket->putInt(fastNetworkPacket.getLogicTypeId());
302  // payload
303  udpClientPacket->putBytes(fastNetworkPacket.getData(), fastNetworkPacket.getPosition());
304  }
305  // datagram size
306  auto datagramSize = udpClientPacket->getSize();
307  if (datagramSize > 1) {
308  if (datagramSize < UDPPacket::PACKET_MAX_SIZE - 17) {
309  // no more network packets, size = 0
310  udpClientPacket->putByte(0);
311  }
312  udpClient->sendMessage(udpClient->createMessage(udpClientPacket.release()), true);
313  }
314  //
315  fastLogicNetworkPackets.clear();
316  }
317  }
318 
319  // get some rest
320  int64_t timeDelta = Time::getCurrentMillis() - timeLast;
321  if (timeDelta > 33) {
322  Console::println("ApplicationClient::run(): time delta < 33FPS, it took " + to_string(timeDelta) + " ms to compute");
323  timeDelta = 33;
324  } else
325  if (timeDelta < 16) {
326  timeDelta = 16;
327  }
328  // update world if context is initialized
329  if (context->isInitialized() == true) {
330  context->getWorld()->update(timeDelta / 1000.0f);
331  }
332  timeDelta = Time::getCurrentMillis() - timeLast;
333  if (timeDelta < 16) {
334  Thread::sleep(16 - timeDelta);
335  }
336  timeLast = Time::getCurrentMillis();
337  }
338 
339  //
340  Console::println("ApplicationClient::run(): done");
341 }
342 
343 void ApplicationClient::handleInNetworkPackets(const vector<Logic*>& logics, vector<LogicNetworkPacket>& inLogicNetworkPackets) {
344  // TODO: improve me
345  for (auto& logicNetworkPacket: inLogicNetworkPackets) {
346  for (auto logic: logics) {
347  NetworkLogic* networkLogic = dynamic_cast<NetworkLogic*>(logic);
348  if (networkLogic != nullptr) {
349  if (networkLogic->getNetworkPacketTypeId() == logicNetworkPacket.getLogicTypeId()) {
350  logicNetworkPacket.reset();
351  networkLogic->handleNetworkPacket(logicNetworkPacket);
352  }
353  }
354  }
355  }
356 }
357 
358 void ApplicationClient::handleHIDEvents(vector<GUIMouseEvent>& mouseEvents, vector<GUIKeyboardEvent>& keyEvents) {
359  mutex.lock();
360  for (auto logic: context->getLogics()) {
361  if (logic->isHandlingHIDInput() == true) {
362  logic->handleHIDEvents(mouseEvents, keyEvents);
363  }
364  }
365  //
366  mutex.unlock();
367 }
368 
370  auto now = Time::getCurrentMillis();
371  // execute update engine of logics
372  mutex.lock();
373  context->initUpdateEngine();
374  vector<Logic::QueuedSound> requeueSounds;
375  for (auto logic: context->getLogics()) {
376  logic->updateEngine();
377  // fetch sounds
378  auto gameLogicId = logic->getId();
379  for (auto& queuedSound: logic->getQueuedSounds()) {
380  if (now > queuedSound.timeIssuedAt + static_cast<int64_t>(queuedSound.timeDelay)) {
381  queuedSounds.emplace_back(
382  gameLogicId,
383  queuedSound.id,
384  queuedSound.gain,
385  queuedSound.pitch,
386  queuedSound.ignoreIfPlaying,
387  queuedSound.attachedToLogic,
388  queuedSound.position,
389  0.0f
390  );
391  } else{
392  requeueSounds.push_back(queuedSound);
393  }
394  }
395  logic->setQueuedSounds(requeueSounds);
396  requeueSounds.clear();
397  }
398  //
399  context->doneUpdateEngine();
400  mutex.unlock();
401 
402  // audio
403  auto engine = context->getEngine();
404  auto audio = context->getAudio();
405  const auto& cameraPosition = engine->getCamera()->getLookFrom();
406  for (auto& queuedSound: queuedSounds) {
407  if (queuedSound.attachedToLogic == true) {
408  auto entity = engine->getEntity(queuedSound.gameLogicId);
409  if (entity == nullptr) {
410  // Console::println("WS::display(): " + queuedSound.gameLogicId + ": " + queuedSound.id + ": engine entity not found");
411  queuedSound.distanceFromCamera = 100.0f * 100.0f;
412  } else {
413  queuedSound.position = entity->getTranslation();
414  }
415  }
416  queuedSound.distanceFromCamera = queuedSound.position.clone().sub(cameraPosition).computeLengthSquared();
417  }
418  sort(
419  queuedSounds.begin(),
420  queuedSounds.end(),
421  [](const QueuedSound& queuedSound1, const QueuedSound& queuedSound2) {
422  return queuedSound1.distanceFromCamera < queuedSound2.distanceFromCamera;
423  }
424  );
425  for (auto& queuedSound: queuedSounds) {
426  if (queuedSound.distanceFromCamera >= 100.0f * 100.0f) continue;
427  if (queuedSound.ignoreIfPlaying == true) {
428  auto activeSoundKey = queuedSound.gameLogicId + "." + queuedSound.id;
429  auto activeSoundIt = activeSounds.find(activeSoundKey);
430  if (activeSoundIt != activeSounds.end()) {
431  auto sound = audio->getEntity(activeSoundIt->second.id);
432  if (sound != nullptr && sound->isPlaying() == true) {
433  sound->setSourcePosition(queuedSound.position);
434  sound->setGain(audioGain * queuedSound.gain);
435  sound->setPitch(queuedSound.pitch);
436  continue;
437  }
438  }
439  }
440  for (auto i = 0; i < context->getSoundPoolSize(); i++) {
441  auto sound = audio->getEntity(queuedSound.id + (context->getSoundPoolSize() == 1?"":"." + to_string(i)));
442  if (sound == nullptr) {
443  Console::println("WS::display(): " + queuedSound.gameLogicId + ": " + queuedSound.id + ": sound not found");
444  continue;
445  }
446  if (sound->isPlaying() == true) {
447  continue;
448  }
449  sound->setGain(audioGain * queuedSound.gain);
450  sound->setSourcePosition(queuedSound.position);
451  sound->setPitch(queuedSound.pitch);
452  sound->play();
453  if (queuedSound.ignoreIfPlaying == true) {
454  auto activeSoundKey = queuedSound.gameLogicId + "." + queuedSound.id;
455  activeSounds[activeSoundKey] = {
456  .id = sound->getId(),
457  .attachedToLogic = queuedSound.attachedToLogic,
458  .gameLogicId = queuedSound.gameLogicId
459  };
460  }
461  break;
462  }
463  }
464  vector<string> inactiveSounds;
465  for (const auto& [activeSoundKey, activeSound]: activeSounds) {
466  auto sound = audio->getEntity(activeSound.id);
467  if (sound->isPlaying() == false) {
468  inactiveSounds.push_back(activeSoundKey);
469  continue;
470  }
471  if (activeSound.attachedToLogic == true) {
472  auto entity = engine->getEntity(activeSound.gameLogicId);
473  if (entity != nullptr) sound->setSourcePosition(entity->getTranslation());
474  }
475  }
476  for (auto& inactiveSound: inactiveSounds) {
477  activeSounds.erase(inactiveSound);
478  }
479  queuedSounds.clear();
480 }
Audio entity base class.
Definition: AudioEntity.h:19
Interface to audio module.
Definition: Audio.h:29
Engine entity.
Definition: Entity.h:30
virtual ~ApplicationClient()
Public constructor.
void handleInNetworkPackets(const vector< Logic * > &logics, vector< LogicNetworkPacket > &inLogicNetworkPackets)
Handle in logic network packets.
unordered_map< string, ActiveSound > activeSounds
void update()
Updates engine and audio to context engine and audio instances.
void handleHIDEvents(vector< GUIMouseEvent > &mouseEvents, vector< GUIKeyboardEvent > &keyEvents)
Collect HID events to pass to logics that have handling HID events enabled.
virtual void run() override
Abstract run() method, should be implemented by subclassed class, will be called after spawn by start...
void setLogicsMutex(Mutex *logicsMutex)
Set logics mutex.
Definition: Context.h:429
static constexpr uint32_t LOGIC_TYPEID_NONE
vector< LogicNetworkPacket > & getNetworkPackets()
Get outgoing network packets.
virtual uint32_t getNetworkPacketTypeId()=0
virtual void handleNetworkPacket(LogicNetworkPacket &packet)=0
Handle network packet.
Dynamic physics world class.
Definition: World.h:38
Mutex implementation.
Definition: Mutex.h:19
void unlock()
Unlocks this mutex.
Definition: Mutex.h:54
void lock()
Locks the mutex, additionally mutex locks will block until other locks have been unlocked.
Definition: Mutex.h:47
Base class for threads.
Definition: Thread.h:20
bool isStopRequested()
Returns if stop has been requested.
Definition: Thread.h:77
Console class.
Definition: Console.h:29
Time utility class.
Definition: Time.h:20