Panda3D
 All Classes Functions Variables Enumerations
cConnectionRepository.cxx
1 // Filename: cConnectionRepository.cxx
2 // Created by: drose (17May04)
3 //
4 ////////////////////////////////////////////////////////////////////
5 //
6 // PANDA 3D SOFTWARE
7 // Copyright (c) Carnegie Mellon University. All rights reserved.
8 //
9 // All use of this software is subject to the terms of the revised BSD
10 // license. You should have received a copy of this license along
11 // with this source code in a file named "LICENSE."
12 //
13 ////////////////////////////////////////////////////////////////////
14 
15 #include "cConnectionRepository.h"
16 #include "dcmsgtypes.h"
17 #include "dcClass.h"
18 #include "dcPacker.h"
19 
20 #include "config_distributed.h"
21 #include "config_downloader.h"
22 #include "httpChannel.h"
23 #include "urlSpec.h"
24 #include "datagramIterator.h"
25 #include "throw_event.h"
26 #include "pStatTimer.h"
27 
28 #ifdef HAVE_PYTHON
29 #include "py_panda.h"
30 #endif
31 
32 const string CConnectionRepository::_overflow_event_name = "CRDatagramOverflow";
33 
34 #ifndef CPPPARSER
35 PStatCollector CConnectionRepository::_update_pcollector("App:Show code:readerPollTask:Update");
36 #endif // CPPPARSER
37 
38 ////////////////////////////////////////////////////////////////////
39 // Function: CConnectionRepository::Constructor
40 // Access: Published
41 // Description:
42 ////////////////////////////////////////////////////////////////////
43 CConnectionRepository::
44 CConnectionRepository(bool has_owner_view, bool threaded_net) :
45  _lock("CConnectionRepository::_lock"),
46 #ifdef HAVE_PYTHON
47  _python_repository(NULL),
48 #endif
49 #ifdef HAVE_OPENSSL
50  _http_conn(NULL),
51 #endif
52 #ifdef HAVE_NET
53  _cw(&_qcm, threaded_net ? 1 : 0),
54  _qcr(&_qcm, threaded_net ? 1 : 0),
55 #endif
56 #ifdef WANT_NATIVE_NET
57  _bdc(4096000,4096000,1400),
58  _native(false),
59 #endif
60  _client_datagram(true),
61  _handle_datagrams_internally(handle_datagrams_internally),
62  _simulated_disconnect(false),
63  _verbose(distributed_cat.is_spam()),
64  _time_warning(0.0),
65 // _msg_channels(),
66  _msg_sender(0),
67  _msg_type(0),
68  _has_owner_view(has_owner_view),
69  _handle_c_updates(true),
70  _want_message_bundling(true),
71  _bundling_msgs(0),
72  _in_quiet_zone(0)
73 {
74 #if defined(HAVE_NET) && defined(SIMULATE_NETWORK_DELAY)
75  if (min_lag != 0.0 || max_lag != 0.0) {
76  _qcr.start_delay(min_lag, max_lag);
77  }
78 #endif
79  _tcp_header_size = tcp_header_size;
80 }
81 
82 ////////////////////////////////////////////////////////////////////
83 // Function: CConnectionRepository::Destructor
84 // Access: Published
85 // Description:
86 ////////////////////////////////////////////////////////////////////
87 CConnectionRepository::
88 ~CConnectionRepository() {
89  disconnect();
90 }
91 
92 ////////////////////////////////////////////////////////////////////
93 // Function: CConnectionRepository::set_tcp_header_size
94 // Access: Public
95 // Description: Sets the header size of TCP packets. At the present,
96 // legal values for this are 0, 2, or 4; this specifies
97 // the number of bytes to use encode the datagram length
98 // at the start of each TCP datagram. Sender and
99 // receiver must independently agree on this.
100 ////////////////////////////////////////////////////////////////////
102 set_tcp_header_size(int tcp_header_size) {
103  _tcp_header_size = tcp_header_size;
104 
105 #ifdef HAVE_OPENSSL
106  if (_http_conn != (SocketStream *)NULL) {
107  _http_conn->set_tcp_header_size(tcp_header_size);
108  }
109 #endif
110 
111 #ifdef HAVE_NET
112  _cw.set_tcp_header_size(tcp_header_size);
113  _qcr.set_tcp_header_size(tcp_header_size);
114 #endif
115 }
116 
117 #ifdef HAVE_OPENSSL
118 ////////////////////////////////////////////////////////////////////
119 // Function: CConnectionRepository::set_connection_http
120 // Access: Published
121 // Description: Once a connection has been established via the HTTP
122 // interface, gets the connection and uses it. The
123 // supplied HTTPChannel object must have a connection
124 // available via get_connection().
125 ////////////////////////////////////////////////////////////////////
126 void CConnectionRepository::
127 set_connection_http(HTTPChannel *channel) {
128  ReMutexHolder holder(_lock);
129 
130  disconnect();
131  nassertv(channel->is_connection_ready());
132  _http_conn = channel->get_connection();
133  _http_conn->set_tcp_header_size(_tcp_header_size);
134 #ifdef SIMULATE_NETWORK_DELAY
135  if (min_lag != 0.0 || max_lag != 0.0) {
136  _http_conn->start_delay(min_lag, max_lag);
137  }
138 #endif
139 }
140 #endif // HAVE_OPENSSL
141 
142 #ifdef HAVE_OPENSSL
143 ////////////////////////////////////////////////////////////////////
144 // Function: CConnectionRepository::get_stream
145 // Access: Published
146 // Description: Returns the SocketStream that internally represents
147 // the already-established HTTP connection. Returns
148 // NULL if there is no current HTTP connection.
149 ////////////////////////////////////////////////////////////////////
150 SocketStream *CConnectionRepository::
151 get_stream() {
152  ReMutexHolder holder(_lock);
153 
154  return _http_conn;
155 }
156 #endif // HAVE_OPENSSL
157 
158 
159 #ifdef HAVE_NET
160 ////////////////////////////////////////////////////////////////////
161 // Function: CConnectionRepository::try_connect_net
162 // Access: Published
163 // Description: Uses Panda's "net" library to try to connect to the
164 // server and port named in the indicated URL. Returns
165 // true if successful, false otherwise.
166 ////////////////////////////////////////////////////////////////////
167 bool CConnectionRepository::
168 try_connect_net(const URLSpec &url) {
169  ReMutexHolder holder(_lock);
170 
171  disconnect();
172 
173  _net_conn =
174  _qcm.open_TCP_client_connection(url.get_server(), url.get_port(),
175  game_server_timeout_ms);
176 
177  if (_net_conn != (Connection *)NULL) {
178  _net_conn->set_no_delay(true);
179  _qcr.add_connection(_net_conn);
180  return true;
181  }
182 
183  return false;
184 }
185 #endif // HAVE_NET
186 
187 #ifdef WANT_NATIVE_NET
188 ////////////////////////////////////////////////////////////////////
189 // Function: CConnectionRepository::connect_native
190 // Access: Published
191 // Description: Connects to the server using Panda's low-level and
192 // fast "native net" library.
193 ////////////////////////////////////////////////////////////////////
194 bool CConnectionRepository::
195 connect_native(const URLSpec &url) {
196  ReMutexHolder holder(_lock);
197 
198  _native=true;
199  Socket_Address addr;
200  addr.set_host(url.get_server(),url.get_port());
201  _bdc.ClearAddresses();
202  _bdc.AddAddress(addr);
203  return _bdc.DoConnect();
204 }
205 
206 #endif //WANT NATIVE NET
207 
208 #ifdef SIMULATE_NETWORK_DELAY
209 ////////////////////////////////////////////////////////////////////
210 // Function: CConnectionRepository::start_delay
211 // Access: Published
212 // Description: Enables a simulated network latency. All datagrams
213 // received from this point on will be held for a random
214 // interval of least min_delay seconds, and no more than
215 // max_delay seconds, before being visible. It is as if
216 // datagrams suddenly took much longer to arrive.
217 //
218 // This should *only* be called if the underlying socket
219 // is non-blocking. If you call this on a blocking
220 // socket, it will force all datagrams to be held up
221 // until the socket closes.
222 //
223 // This has no effect if the connection method is via
224 // the "native net" library.
225 ////////////////////////////////////////////////////////////////////
226 void CConnectionRepository::
227 start_delay(double min_delay, double max_delay) {
228  ReMutexHolder holder(_lock);
229 
230  if (min_delay != 0.0 || max_delay != 0.0) {
231 #ifdef HAVE_NET
232  _qcr.start_delay(min_delay, max_delay);
233 #endif // HAVE_NET
234 #ifdef HAVE_OPENSSL
235  if (_http_conn != (SocketStream *)NULL) {
236  _http_conn->start_delay(min_delay, max_delay);
237  }
238 #endif // HAVE_OPENSSL
239  } else {
240  stop_delay();
241  }
242 }
243 #endif // SIMULATE_NETWORK_DELAY
244 
245 #ifdef SIMULATE_NETWORK_DELAY
246 ////////////////////////////////////////////////////////////////////
247 // Function: CConnectionRepository::stop_delay
248 // Access: Published
249 // Description: Disables the simulated network latency started by a
250 // previous call to start_delay(). Datagrams will once
251 // again be visible as soon as they are received.
252 ////////////////////////////////////////////////////////////////////
253 void CConnectionRepository::
254 stop_delay() {
255  ReMutexHolder holder(_lock);
256 
257 #ifdef HAVE_NET
258  _qcr.stop_delay();
259 #endif // HAVE_NET
260 #ifdef HAVE_OPENSSL
261  if (_http_conn != (SocketStream *)NULL) {
262  _http_conn->stop_delay();
263  }
264 #endif // HAVE_OPENSSL
265 }
266 #endif // SIMULATE_NETWORK_DELAY
267 
268 ////////////////////////////////////////////////////////////////////
269 // Function: CConnectionRepository::check_datagram
270 // Access: Published
271 // Description: Returns true if a new datagram is available, false
272 // otherwise. If the return value is true, the new
273 // datagram may be retrieved via get_datagram(), or
274 // preferably, with get_datagram_iterator() and
275 // get_msg_type().
276 ////////////////////////////////////////////////////////////////////
279  ReMutexHolder holder(_lock);
280 
281  if (_simulated_disconnect) {
282  return false;
283  }
284  #ifdef WANT_NATIVE_NET
285  if(_native)
286  _bdc.Flush();
287  #endif //WANT_NATIVE_NET
288 
289  while (do_check_datagram()) {
290  if (get_verbose()) {
291  describe_message(nout, "RECV", _dg);
292  }
293 
294  // Start breaking apart the datagram.
295  _di = DatagramIterator(_dg);
296 
297  if (!_client_datagram) {
298  unsigned char wc_cnt;
299  wc_cnt = _di.get_uint8();
300  _msg_channels.clear();
301  for (unsigned char lp1 = 0; lp1 < wc_cnt; lp1++) {
302  CHANNEL_TYPE schan = _di.get_uint64();
303  _msg_channels.push_back(schan);
304  }
305  _msg_sender = _di.get_uint64();
306 
307 #ifdef HAVE_PYTHON
308  // For now, we need to stuff this field onto the Python
309  // structure, to support legacy code that expects to find it
310  // there.
311  if (_python_repository != (PyObject *)NULL) {
312 #if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
313  PyGILState_STATE gstate;
314  gstate = PyGILState_Ensure();
315 #endif
316  PyObject *value = PyLong_FromUnsignedLongLong(_msg_sender);
317  PyObject_SetAttrString(_python_repository, "msgSender", value);
318  Py_DECREF(value);
319 #if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
320  PyGILState_Release(gstate);
321 #endif
322  }
323 #endif // HAVE_PYTHON
324  }
325 
326  _msg_type = _di.get_uint16();
327  // Is this a message that we can process directly?
328  if (!_handle_datagrams_internally) {
329  return true;
330  }
331 
332  switch (_msg_type) {
333 #ifdef HAVE_PYTHON
334  case CLIENT_OBJECT_UPDATE_FIELD:
335  case STATESERVER_OBJECT_UPDATE_FIELD:
336  if (_handle_c_updates) {
337  if (_has_owner_view) {
338  if (!handle_update_field_owner()) {
339  return false;
340  }
341  } else {
342  if (!handle_update_field()) {
343  return false;
344  }
345  }
346  } else {
347  // Let the caller (Python) deal with this update.
348  return true;
349  }
350  break;
351 #endif // HAVE_PYTHON
352 
353  default:
354  // Some unknown message; let the caller deal with it.
355  return true;
356  }
357  }
358 
359  // No datagrams available.
360  return false;
361 }
362 
363 ////////////////////////////////////////////////////////////////////
364 // Function: CConnectionRepository::is_connected
365 // Access: Published
366 // Description: Returns true if the connection to the gameserver is
367 // established and still good, false if we are not
368 // connected. A false value means either (a) we never
369 // successfully connected, (b) we explicitly called
370 // disconnect(), or (c) we were connected, but the
371 // connection was spontaneously lost.
372 ////////////////////////////////////////////////////////////////////
375  ReMutexHolder holder(_lock);
376 
377 #ifdef WANT_NATIVE_NET
378  if(_native)
379  return (_bdc.IsConnected());
380 #endif
381 
382 #ifdef HAVE_NET
383  if (_net_conn) {
384  if (_qcm.reset_connection_available()) {
385  PT(Connection) reset_connection;
386  if (_qcm.get_reset_connection(reset_connection)) {
387  _qcm.close_connection(reset_connection);
388  if (reset_connection == _net_conn) {
389  // Whoops, lost our connection.
390  _net_conn = NULL;
391  return false;
392  }
393  }
394  }
395  return true;
396  }
397 #endif // HAVE_NET
398 
399 #ifdef HAVE_OPENSSL
400  if (_http_conn) {
401  if (!_http_conn->is_closed()) {
402  return true;
403  }
404 
405  // Connection lost.
406  delete _http_conn;
407  _http_conn = NULL;
408  }
409 #endif // HAVE_OPENSSL
410 
411  return false;
412 }
413 
414 ////////////////////////////////////////////////////////////////////
415 // Function: CConnectionRepository::send_datagram
416 // Access: Published
417 // Description: Queues the indicated datagram for sending to the
418 // server. It may not get sent immediately if
419 // collect_tcp is in effect; call flush() to guarantee
420 // it is sent now.
421 ////////////////////////////////////////////////////////////////////
424  ReMutexHolder holder(_lock);
425 
426  if (_simulated_disconnect) {
427  distributed_cat.warning()
428  << "Unable to send datagram during simulated disconnect.\n";
429  return false;
430  }
431 
432  if (get_verbose()) {
433  describe_message(nout, "SEND", dg);
434  }
435 
437  bundle_msg(dg);
438  return true;
439  }
440 
441 #ifdef WANT_NATIVE_NET
442  if(_native)
443  return _bdc.SendMessage(dg);
444 #endif
445 
446 #ifdef HAVE_NET
447  if (_net_conn) {
448  _cw.send(dg, _net_conn);
449  return true;
450  }
451 #endif // HAVE_NET
452 
453 #ifdef HAVE_OPENSSL
454  if (_http_conn) {
455  if (!_http_conn->send_datagram(dg)) {
456  distributed_cat.warning()
457  << "Could not send datagram.\n";
458  return false;
459  }
460 
461  return true;
462  }
463 #endif // HAVE_OPENSSL
464 
465  distributed_cat.warning()
466  << "Unable to send datagram after connection is closed.\n";
467  return false;
468 }
469 
470 ////////////////////////////////////////////////////////////////////
471 // Function: CConnectionRepository::start_message_bundle
472 // Access: Published
473 // Description: Send a set of messages to the state server that will
474 // be processed atomically. For instance, you can do a
475 // combined setLocation/setPos and prevent race
476 // conditions where clients briefly get the setLocation
477 // but not the setPos, because the state server hasn't
478 // processed the setPos yet
479 ////////////////////////////////////////////////////////////////////
482  ReMutexHolder holder(_lock);
483 
484  // store up network messages until sendMessageBundle is called
485  // all updates in between must be sent from the same doId (updates
486  // must all affect the same DistributedObject)
487  // it is an error to call this again before calling sendMessageBundle
488  if (get_verbose()) {
489  nout << "CR::SEND:BUNDLE_START(" << _bundling_msgs << ")" << endl;
490  }
491  if (_bundling_msgs == 0) {
492  _bundle_msgs.clear();
493  }
494  ++_bundling_msgs;
495 }
496 
497 ////////////////////////////////////////////////////////////////////
498 // Function: CConnectionRepository::send_message_bundle
499 // Access: Published
500 // Description: Send network messages queued up since
501 // startMessageBundle was called.
502 ////////////////////////////////////////////////////////////////////
504 send_message_bundle(unsigned int channel, unsigned int sender_channel) {
505  ReMutexHolder holder(_lock);
506  nassertv(_bundling_msgs);
507 
508  --_bundling_msgs;
509 
510  if (get_verbose()) {
511  nout << "CR::SEND:BUNDLE_FINISH(" << _bundling_msgs << ")" << endl;
512  }
513 
514  // if _bundling_msgs ref count is zero, send the bundle out
515  if (_bundling_msgs == 0 && get_want_message_bundling()) {
516  Datagram dg;
517  // add server header (see PyDatagram.addServerHeader)
518  dg.add_int8(1);
519  dg.add_uint64(channel);
520  dg.add_uint64(sender_channel);
521  dg.add_uint16(STATESERVER_BOUNCE_MESSAGE);
522  // add each bundled message
523  BundledMsgVector::const_iterator bmi;
524  for (bmi = _bundle_msgs.begin(); bmi != _bundle_msgs.end(); bmi++) {
525  dg.add_string(*bmi);
526  }
527 
528  send_datagram(dg);
529  }
530 }
531 
532 ////////////////////////////////////////////////////////////////////
533 // Function: CConnectionRepository::abandon_message_bundles
534 // Access: Published
535 // Description: throw out any msgs that have been queued up for
536 // message bundles
537 ////////////////////////////////////////////////////////////////////
540  ReMutexHolder holder(_lock);
541 
542  nassertv(is_bundling_messages());
543  _bundling_msgs = 0;
544  _bundle_msgs.clear();
545 }
546 
547 ////////////////////////////////////////////////////////////////////
548 // Function: CConnectionRepository::bundle_msg
549 // Access: Published
550 // Description:
551 ////////////////////////////////////////////////////////////////////
552 void CConnectionRepository::
553 bundle_msg(const Datagram &dg) {
554  ReMutexHolder holder(_lock);
555 
556  nassertv(is_bundling_messages());
557  _bundle_msgs.push_back(dg.get_message());
558 }
559 
560 ////////////////////////////////////////////////////////////////////
561 // Function: CConnectionRepository::consider_flush
562 // Access: Published
563 // Description: Sends the most recently queued data if enough time
564 // has elapsed. This only has meaning if
565 // set_collect_tcp() has been set to true.
566 ////////////////////////////////////////////////////////////////////
569  ReMutexHolder holder(_lock);
570 
571  if (_simulated_disconnect) {
572  return false;
573  }
574 
575 #ifdef WANT_NATIVE_NET
576  if(_native)
577  return true; //Maybe we should just flush here for now?
578 #endif
579 
580 #ifdef HAVE_NET
581  if (_net_conn) {
582  return _net_conn->consider_flush();
583  }
584 #endif // HAVE_NET
585 
586 #ifdef HAVE_OPENSSL
587  if (_http_conn) {
588  return _http_conn->consider_flush();
589  }
590 #endif // HAVE_OPENSSL
591 
592  return false;
593 }
594 
595 ////////////////////////////////////////////////////////////////////
596 // Function: CConnectionRepository::flush
597 // Access: Published
598 // Description: Sends the most recently queued data now. This only
599 // has meaning if set_collect_tcp() has been set to
600 // true.
601 ////////////////////////////////////////////////////////////////////
603 flush() {
604  ReMutexHolder holder(_lock);
605 
606  if (_simulated_disconnect) {
607  return false;
608  }
609  #ifdef WANT_NATIVE_NET
610  if(_native)
611  return _bdc.Flush();
612  #endif
613 
614  #ifdef HAVE_NET
615  if (_net_conn) {
616  return _net_conn->flush();
617  }
618  #endif // HAVE_NET
619 
620  #ifdef HAVE_OPENSSL
621  if (_http_conn) {
622  return _http_conn->flush();
623  }
624  #endif // HAVE_OPENSSL
625 
626  return false;
627 }
628 
629 ////////////////////////////////////////////////////////////////////
630 // Function: CConnectionRepository::disconnect
631 // Access: Published
632 // Description: Closes the connection to the server.
633 ////////////////////////////////////////////////////////////////////
636  ReMutexHolder holder(_lock);
637 
638  #ifdef WANT_NATIVE_NET
639  if(_native) {
640  _bdc.Reset();
641  _bdc.ClearAddresses();
642  }
643  #endif
644  #ifdef HAVE_NET
645  if (_net_conn) {
646  _qcm.close_connection(_net_conn);
647  _net_conn = NULL;
648  }
649  #endif // HAVE_NET
650 
651  #ifdef HAVE_OPENSSL
652  if (_http_conn) {
653  _http_conn->close();
654  delete _http_conn;
655  _http_conn = NULL;
656  }
657  #endif // HAVE_OPENSSL
658 
659  _simulated_disconnect = false;
660 }
661 
662 ////////////////////////////////////////////////////////////////////
663 // Function: CConnectionRepository::shutdown
664 // Access: Published
665 // Description: May be called at application shutdown to ensure all
666 // threads are cleaned up.
667 ////////////////////////////////////////////////////////////////////
670  disconnect();
671 
672  #ifdef HAVE_NET
673  _cw.shutdown();
674  _qcr.shutdown();
675  #endif // HAVE_NET
676 }
677 
678 ////////////////////////////////////////////////////////////////////
679 // Function: CConnectionRepository::do_check_datagram
680 // Access: Private
681 // Description: The private implementation of check_datagram(), this
682 // gets one datagram if it is available.
683 ////////////////////////////////////////////////////////////////////
684 bool CConnectionRepository::
685 do_check_datagram() {
686  #ifdef WANT_NATIVE_NET
687  if(_native) {
688  return _bdc.GetMessage(_dg);
689  }
690  #endif
691  #ifdef HAVE_NET
692  if (_net_conn) {
693  _net_conn->consider_flush();
694  if (_qcr.get_overflow_flag()) {
695  throw_event(get_overflow_event_name());
696  _qcr.reset_overflow_flag();
697  }
698  return (_qcr.data_available() && _qcr.get_data(_dg));
699  }
700  #endif // HAVE_NET
701 
702  #ifdef HAVE_OPENSSL
703  if (_http_conn) {
704  _http_conn->consider_flush();
705  return _http_conn->receive_datagram(_dg);
706  }
707  #endif // HAVE_OPENSSL
708 
709 
710  return false;
711 }
712 
713 ////////////////////////////////////////////////////////////////////
714 // Function: CConnectionRepository::handle_update_field
715 // Access: Private
716 // Description: Directly handles an update message on a field.
717 // Python never touches the datagram; it just gets its
718 // distributed method called with the appropriate
719 // parameters. Returns true if everything is ok, false
720 // if there was an error processing the field's update
721 // method.
722 ////////////////////////////////////////////////////////////////////
723 bool CConnectionRepository::
724 handle_update_field() {
725 #ifdef HAVE_PYTHON
726 #if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
727  PyGILState_STATE gstate;
728  gstate = PyGILState_Ensure();
729 #endif
730 
731  PStatTimer timer(_update_pcollector);
732  unsigned int do_id = _di.get_uint32();
733  if (_python_repository != (PyObject *)NULL)
734  {
735  PyObject *doId2do =
736  PyObject_GetAttrString(_python_repository, "doId2do");
737  nassertr(doId2do != NULL, false);
738 
739  #ifdef USE_PYTHON_2_2_OR_EARLIER
740  PyObject *doId = PyInt_FromLong(do_id);
741  #else
742  PyObject *doId = PyLong_FromUnsignedLong(do_id);
743  #endif
744  PyObject *distobj = PyDict_GetItem(doId2do, doId);
745  Py_DECREF(doId);
746  Py_DECREF(doId2do);
747 
748  if (distobj != NULL) {
749  PyObject *dclass_obj = PyObject_GetAttrString(distobj, "dclass");
750  nassertr(dclass_obj != NULL, false);
751 
752 
753  PyObject *dclass_this = PyObject_GetAttrString(dclass_obj, "this");
754  Py_DECREF(dclass_obj);
755  nassertr(dclass_this != NULL, false);
756 
757  DCClass *dclass = (DCClass *)PyLong_AsLong(dclass_this);
758  Py_DECREF(dclass_this);
759 
760  // If in quiet zone mode, throw update away unless distobj
761  // has 'neverDisable' attribute set to non-zero
762  if (_in_quiet_zone) {
763  PyObject *neverDisable = PyObject_GetAttrString(distobj, "neverDisable");
764  nassertr(neverDisable != NULL, false);
765 
766  unsigned int cNeverDisable = PyLong_AsLong(neverDisable);
767  if (!cNeverDisable) {
768  // in quiet zone and distobj is disable-able
769  // drop update on the floor
770 #if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
771  PyGILState_Release(gstate);
772 #endif
773  return true;
774  }
775  }
776 
777  // It's a good idea to ensure the reference count to distobj is
778  // raised while we call the update method--otherwise, the update
779  // method might get into trouble if it tried to delete the
780  // object from the doId2do map.
781  Py_INCREF(distobj);
782  dclass->receive_update(distobj, _di);
783  Py_DECREF(distobj);
784 
785  if (PyErr_Occurred()) {
786 #if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
787  PyGILState_Release(gstate);
788 #endif
789  return false;
790  }
791  }
792 
793  }
794 
795 #if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
796  PyGILState_Release(gstate);
797 #endif
798  #endif // HAVE_PYTHON
799  return true;
800 }
801 
802 
803 ////////////////////////////////////////////////////////////////////
804 // Function: CConnectionRepository::handle_update_field_owner
805 // Access: Private
806 // Description: Directly handles an update message on a field.
807 // Supports 'owner' views of objects, separate from 'visible'
808 // view, and forwards fields to the appropriate view(s) based
809 // on DC flags. Python never touches the datagram; it just
810 // gets its distributed method called with the appropriate
811 // parameters. Returns true if everything is ok, false if
812 // there was an error processing the field's update method.
813 ////////////////////////////////////////////////////////////////////
814 bool CConnectionRepository::
815 handle_update_field_owner() {
816 #ifdef HAVE_PYTHON
817 #if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
818  PyGILState_STATE gstate;
819  gstate = PyGILState_Ensure();
820 #endif
821 
822  PStatTimer timer(_update_pcollector);
823  unsigned int do_id = _di.get_uint32();
824  if (_python_repository != (PyObject *)NULL) {
825  PyObject *doId2do =
826  PyObject_GetAttrString(_python_repository, "doId2do");
827  nassertr(doId2do != NULL, false);
828 
829  PyObject *doId2ownerView =
830  PyObject_GetAttrString(_python_repository, "doId2ownerView");
831  nassertr(doId2ownerView != NULL, false);
832 
833  #ifdef USE_PYTHON_2_2_OR_EARLIER
834  PyObject *doId = PyInt_FromLong(do_id);
835  #else
836  PyObject *doId = PyLong_FromUnsignedLong(do_id);
837  #endif
838 
839  // pass the update to the owner view first
840  PyObject *distobjOV = PyDict_GetItem(doId2ownerView, doId);
841  Py_DECREF(doId2ownerView);
842 
843  if (distobjOV != NULL) {
844  PyObject *dclass_obj = PyObject_GetAttrString(distobjOV, "dclass");
845  nassertr(dclass_obj != NULL, false);
846 
847  PyObject *dclass_this = PyObject_GetAttrString(dclass_obj, "this");
848  Py_DECREF(dclass_obj);
849  nassertr(dclass_this != NULL, false);
850 
851  DCClass *dclass = (DCClass *)PyLong_AsLong(dclass_this);
852  Py_DECREF(dclass_this);
853 
854  // check if we should forward this update to the owner view
855  DCPacker packer;
856  packer.set_unpack_data(_di.get_remaining_bytes());
857  int field_id = packer.raw_unpack_uint16();
858  DCField *field = dclass->get_field_by_index(field_id);
859  if (field->is_ownrecv()) {
860  // It's a good idea to ensure the reference count to distobjOV is
861  // raised while we call the update method--otherwise, the update
862  // method might get into trouble if it tried to delete the
863  // object from the doId2do map.
864  Py_INCREF(distobjOV);
865  // make a copy of the datagram iterator so that we can use the main
866  // iterator for the non-owner update
867  DatagramIterator _odi(_di);
868  dclass->receive_update(distobjOV, _odi);
869  Py_DECREF(distobjOV);
870 
871  if (PyErr_Occurred()) {
872 #if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
873  PyGILState_Release(gstate);
874 #endif
875  return false;
876  }
877  }
878  }
879 
880  // now pass the update to the visible view
881  PyObject *distobj = PyDict_GetItem(doId2do, doId);
882  Py_DECREF(doId);
883  Py_DECREF(doId2do);
884 
885  if (distobj != NULL) {
886  PyObject *dclass_obj = PyObject_GetAttrString(distobj, "dclass");
887  nassertr(dclass_obj != NULL, false);
888 
889  PyObject *dclass_this = PyObject_GetAttrString(dclass_obj, "this");
890  Py_DECREF(dclass_obj);
891  nassertr(dclass_this != NULL, false);
892 
893  DCClass *dclass = (DCClass *)PyLong_AsLong(dclass_this);
894  Py_DECREF(dclass_this);
895 
896  // check if we should forward this update to the owner view
897  DCPacker packer;
898  packer.set_unpack_data(_di.get_remaining_bytes());
899  int field_id = packer.raw_unpack_uint16();
900  DCField *field = dclass->get_field_by_index(field_id);
901  if (true) {//field->is_broadcast()) {
902  // It's a good idea to ensure the reference count to distobj is
903  // raised while we call the update method--otherwise, the update
904  // method might get into trouble if it tried to delete the
905  // object from the doId2do map.
906  Py_INCREF(distobj);
907  dclass->receive_update(distobj, _di);
908  Py_DECREF(distobj);
909 
910  if (PyErr_Occurred()) {
911 #if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
912  PyGILState_Release(gstate);
913 #endif
914  return false;
915  }
916  }
917  }
918  }
919 
920 #if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
921  PyGILState_Release(gstate);
922 #endif
923 #endif // HAVE_PYTHON
924 
925  return true;
926 }
927 
928 ////////////////////////////////////////////////////////////////////
929 // Function: CConnectionRepository::describe_message
930 // Access: Private
931 // Description: Unpacks the message and reformats it for user
932 // consumption, writing a description on the indicated
933 // output stream.
934 ////////////////////////////////////////////////////////////////////
935 void CConnectionRepository::
936 describe_message(ostream &out, const string &prefix,
937  const Datagram &dg) const {
938  DCPacker packer;
939 
940  packer.set_unpack_data(dg.get_message());
941  CHANNEL_TYPE do_id;
942  int msg_type;
943  bool is_update = false;
944  string full_prefix = "CR::" + prefix;
945 
946  if (!_client_datagram)
947  {
948  unsigned char mcnt = packer.raw_unpack_uint8();
949  for( ;mcnt > 0; mcnt--)
950  packer.RAW_UNPACK_CHANNEL(); // msg_channel
951 
952  packer.RAW_UNPACK_CHANNEL(); // msg_sender
953  msg_type = packer.raw_unpack_uint16();
954  is_update = (msg_type == STATESERVER_OBJECT_UPDATE_FIELD);
955 
956  } else {
957  msg_type = packer.raw_unpack_uint16();
958  is_update = (msg_type == CLIENT_OBJECT_UPDATE_FIELD);
959  }
960 
961  if (!is_update) {
962  // figure out the name of the message
963  // TODO: print out the arguments to the message
964  string msgName;
965 
966  #ifdef HAVE_PYTHON
967  if (_python_repository != (PyObject *)NULL) {
968  PyObject *msgId = PyLong_FromLong(msg_type);
969  nassertv(msgId != NULL);
970 #if PY_MAJOR_VERSION >= 3
971  PyObject *methodName = PyUnicode_FromString("_getMsgName");
972 #else
973  PyObject *methodName = PyString_FromString("_getMsgName");
974 #endif
975  nassertv(methodName != NULL);
976 
977  PyObject *result = PyObject_CallMethodObjArgs(_python_repository, methodName,
978  msgId, NULL);
979  nassertv(result != NULL);
980 
981 #if PY_MAJOR_VERSION >= 3
982  msgName += string(PyUnicode_AsUTF8(result));
983 #else
984  msgName += string(PyString_AsString(result));
985 #endif
986 
987  Py_DECREF(methodName);
988  Py_DECREF(msgId);
989  Py_DECREF(result);
990  }
991  #endif
992  if (msgName.length() == 0) {
993  msgName += "unknown message ";
994  msgName += msg_type;
995  msgName += "\n";
996  }
997  out << full_prefix << ":" << msgName << "\n";
998  dg.dump_hex(out, 2);
999 
1000  } else {
1001  // It's an update message. Figure out what dclass the object is
1002  // based on its doId, so we can decode the rest of the message.
1003  do_id = packer.raw_unpack_uint32();
1004  DCClass *dclass = NULL;
1005 
1006  #ifdef HAVE_PYTHON
1007  if (_python_repository != (PyObject *)NULL) {
1008  PyObject *doId2do =
1009  PyObject_GetAttrString(_python_repository, "doId2do");
1010  nassertv(doId2do != NULL);
1011 
1012  #ifdef USE_PYTHON_2_2_OR_EARLIER
1013  PyObject *doId = PyInt_FromLong(do_id);
1014  #else
1015  PyObject *doId = PyLong_FromUnsignedLong(do_id);
1016  #endif
1017  PyObject *distobj = PyDict_GetItem(doId2do, doId);
1018  Py_DECREF(doId);
1019  Py_DECREF(doId2do);
1020 
1021  if (distobj != NULL) {
1022  PyObject *dclass_obj = PyObject_GetAttrString(distobj, "dclass");
1023  nassertv(dclass_obj != NULL);
1024 
1025  PyObject *dclass_this = PyObject_GetAttrString(dclass_obj, "this");
1026  Py_DECREF(dclass_obj);
1027  nassertv(dclass_this != NULL);
1028 
1029  dclass = (DCClass *)PyLong_AsLong(dclass_this);
1030  Py_DECREF(dclass_this);
1031  }
1032  }
1033  #endif // HAVE_PYTHON
1034 
1035  int field_id = packer.raw_unpack_uint16();
1036 
1037  if (dclass == (DCClass *)NULL) {
1038  out << full_prefix << "update for unknown object " << do_id
1039  << ", field " << field_id << "\n";
1040 
1041  } else {
1042  out << full_prefix <<
1043  ":" << dclass->get_name() << "(" << do_id << ").";
1044  DCField *field = dclass->get_field_by_index(field_id);
1045  if (field == (DCField *)NULL) {
1046  out << "unknown field " << field_id << "\n";
1047 
1048  } else {
1049  out << field->get_name();
1050  packer.begin_unpack(field);
1051  packer.unpack_and_format(out);
1052  packer.end_unpack();
1053  out << "\n";
1054  }
1055  }
1056  }
1057 }
string get_message() const
Returns the datagram&#39;s data as a string.
Definition: datagram.I:431
const string & get_name() const
Returns the name of this field, or empty string if the field is unnamed.
void add_string(const string &str)
Adds a variable-length string to the datagram.
Definition: datagram.I:351
A container for a URL, e.g.
Definition: urlSpec.h:29
const string & get_name() const
Returns the name of this class.
Definition: dcClass.I:32
void abandon_message_bundles()
throw out any msgs that have been queued up for message bundles
void add_int8(PN_int8 value)
Adds a signed 8-bit integer to the datagram.
Definition: datagram.I:128
A single field of a Distributed Class, either atomic or molecular.
Definition: dcField.h:40
unsigned int raw_unpack_uint16()
Unpacks the data from the buffer between unpacking sessions.
Definition: dcPacker.I:1036
void disconnect()
Closes the connection to the server.
bool send_datagram(const Datagram &dg)
Queues the indicated datagram for sending to the server.
bool get_want_message_bundling() const
Returns true if message bundling enabled.
Defines a particular DistributedClass as read from an input .dc file.
Definition: dcClass.h:47
bool consider_flush()
Sends the most recently queued data if enough time has elapsed.
void start_message_bundle()
Send a set of messages to the state server that will be processed atomically.
A lightweight class that can be used to automatically start and stop a PStatCollector around a sectio...
Definition: pStatTimer.h:34
PN_uint8 get_uint8()
Extracts an unsigned 8-bit integer.
PN_uint32 get_uint32()
Extracts an unsigned 32-bit integer.
PN_uint16 get_uint16()
Extracts an unsigned 16-bit integer.
bool is_connected()
Returns true if the connection to the gameserver is established and still good, false if we are not c...
string get_remaining_bytes() const
Returns the remaining bytes in the datagram as a string, but does not extract them from the iterator...
A lightweight class that represents a single element that may be timed and/or counted via stats...
unsigned int raw_unpack_uint8()
Unpacks the data from the buffer between unpacking sessions.
Definition: dcPacker.I:1023
bool is_bundling_messages() const
Returns true if repository is queueing outgoing messages into a message bundle.
void dump_hex(ostream &out, unsigned int indent=0) const
Writes a representation of the entire datagram contents, as a sequence of hex (and ASCII) values...
Definition: datagram.cxx:52
unsigned int raw_unpack_uint32()
Unpacks the data from the buffer between unpacking sessions.
Definition: dcPacker.I:1049
void add_uint64(PN_uint64 value)
Adds an unsigned 64-bit integer to the datagram.
Definition: datagram.I:203
int get_port() const
Returns the port number specified by the URL, or the default port if not specified.
Definition: urlSpec.cxx:84
Similar to MutexHolder, but for a reentrant mutex.
Definition: reMutexHolder.h:27
string unpack_and_format(bool show_field_names=true)
Unpacks an object and formats its value into a syntax suitable for parsing in the dc file (e...
Definition: dcPacker.cxx:1056
A simple place to store and munipulate tcp and port address for communication layer.
void add_uint16(PN_uint16 value)
Adds an unsigned 16-bit integer to the datagram.
Definition: datagram.I:181
bool set_host(const std::string &hostname, int port)
This function will take a port and string-based TCP address and initialize the address with this info...
string get_server() const
Returns the server name specified by the URL, if any.
Definition: urlSpec.I:198
void shutdown()
May be called at application shutdown to ensure all threads are cleaned up.
void set_tcp_header_size(int tcp_header_size)
Sets the header size of TCP packets.
This class can be used for packing a series of numeric and string data into a binary stream...
Definition: dcPacker.h:38
void send_message_bundle(unsigned int channel, unsigned int sender_channel)
Send network messages queued up since startMessageBundle was called.
A class to retrieve the individual data elements previously stored in a Datagram. ...
PN_uint64 get_uint64()
Extracts an unsigned 64-bit integer.
bool flush()
Sends the most recently queued data now.
void set_unpack_data(const string &data)
Sets up the unpack_data pointer.
Definition: dcPacker.cxx:125
Represents a single TCP or UDP socket for input or output.
Definition: connection.h:32
bool check_datagram()
Returns true if a new datagram is available, false otherwise.
An ordered list of data elements, formatted in memory for transmission over a socket or writing to a ...
Definition: datagram.h:43
bool get_verbose() const
Returns the current setting of the verbose flag.
bool is_ownrecv() const
Returns true if the &quot;ownrecv&quot; flag is set for this field, false otherwise.
Definition: dcField.I:165
void begin_unpack(const DCPackerInterface *root)
Begins an unpacking session.
Definition: dcPacker.cxx:168
static const string & get_overflow_event_name()
Returns event string that will be thrown if the datagram reader queue overflows.
bool end_unpack()
Finishes the unpacking session.
Definition: dcPacker.cxx:197
DCField * get_field_by_index(int index_number) const
Returns a pointer to the DCField that has the indicated index number.
Definition: dcClass.cxx:253