Panda3D
cDistributedSmoothNodeBase.cxx
Go to the documentation of this file.
1 /**
2  * PANDA 3D SOFTWARE
3  * Copyright (c) Carnegie Mellon University. All rights reserved.
4  *
5  * All use of this software is subject to the terms of the revised BSD
6  * license. You should have received a copy of this license along
7  * with this source code in a file named "LICENSE."
8  *
9  * @file cDistributedSmoothNodeBase.cxx
10  * @author drose
11  * @date 2004-09-03
12  */
13 
15 #include "cConnectionRepository.h"
16 #include "dcField.h"
17 #include "dcClass.h"
18 #include "dcmsgtypes.h"
19 #include "config_distributed.h"
20 
21 static const PN_stdfloat smooth_node_epsilon = 0.01;
22 static const double network_time_precision = 100.0; // Matches ClockDelta.py
23 
24 /**
25  *
26  */
27 CDistributedSmoothNodeBase::
28 CDistributedSmoothNodeBase() {
29  _repository = nullptr;
30  _is_ai = false;
31  _ai_id = 0;
32 
33 #ifdef HAVE_PYTHON
34  _clock_delta = nullptr;
35 #endif
36 
37  _currL[0] = 0;
38  _currL[1] = 0;
39 }
40 
41 /**
42  *
43  */
44 CDistributedSmoothNodeBase::
45 ~CDistributedSmoothNodeBase() {
46 }
47 
48 /**
49  * Initializes the internal structures from some constructs that are normally
50  * stored only in Python. Also reads the current node's pos & hpr values in
51  * preparation for transmitting them via one of the broadcast_pos_hpr_*()
52  * methods.
53  */
55 initialize(const NodePath &node_path, DCClass *dclass, CHANNEL_TYPE do_id) {
56  _node_path = node_path;
57  _dclass = dclass;
58  _do_id = do_id;
59 
60  nassertv(!_node_path.is_empty());
61 
62  _store_xyz = _node_path.get_pos();
63  _store_hpr = _node_path.get_hpr();
64  _store_stop = false;
65 }
66 
67 /**
68  * Broadcasts the current pos/hpr in its complete form.
69  */
72  _currL[0] = _currL[1];
73  d_setSmPosHprL(_store_xyz[0], _store_xyz[1], _store_xyz[2],
74  _store_hpr[0], _store_hpr[1], _store_hpr[2], _currL[0]);
75 }
76 
77 /**
78  * Examines the complete pos/hpr information to see which of the six elements
79  * have changed, and broadcasts the appropriate messages.
80  */
83  LPoint3 xyz = _node_path.get_pos();
84  LVecBase3 hpr = _node_path.get_hpr();
85 
86  int flags = 0;
87 
88  if (!IS_THRESHOLD_EQUAL(_store_xyz[0], xyz[0], smooth_node_epsilon)) {
89  _store_xyz[0] = xyz[0];
90  flags |= F_new_x;
91  }
92 
93  if (!IS_THRESHOLD_EQUAL(_store_xyz[1], xyz[1], smooth_node_epsilon)) {
94  _store_xyz[1] = xyz[1];
95  flags |= F_new_y;
96  }
97 
98  if (!IS_THRESHOLD_EQUAL(_store_xyz[2], xyz[2], smooth_node_epsilon)) {
99  _store_xyz[2] = xyz[2];
100  flags |= F_new_z;
101  }
102 
103  if (!IS_THRESHOLD_EQUAL(_store_hpr[0], hpr[0], smooth_node_epsilon)) {
104  _store_hpr[0] = hpr[0];
105  flags |= F_new_h;
106  }
107 
108  if (!IS_THRESHOLD_EQUAL(_store_hpr[1], hpr[1], smooth_node_epsilon)) {
109  _store_hpr[1] = hpr[1];
110  flags |= F_new_p;
111  }
112 
113  if (!IS_THRESHOLD_EQUAL(_store_hpr[2], hpr[2], smooth_node_epsilon)) {
114  _store_hpr[2] = hpr[2];
115  flags |= F_new_r;
116  }
117 
118  if (_currL[0] != _currL[1]) {
119  // location (zoneId) has changed, send out all info copy over 'set'
120  // location over to 'sent' location
121  _currL[0] = _currL[1];
122  // Any other change
123  _store_stop = false;
124  d_setSmPosHprL(_store_xyz[0], _store_xyz[1], _store_xyz[2],
125  _store_hpr[0], _store_hpr[1], _store_hpr[2], _currL[0]);
126 
127  } else if (flags == 0) {
128  // No change. Send one and only one "stop" message.
129  if (!_store_stop) {
130  _store_stop = true;
131  d_setSmStop();
132  }
133 
134  } else if (only_changed(flags, F_new_h)) {
135  // Only change in H.
136  _store_stop = false;
137  d_setSmH(_store_hpr[0]);
138 
139  } else if (only_changed(flags, F_new_z)) {
140  // Only change in Z.
141  _store_stop = false;
142  d_setSmZ(_store_xyz[2]);
143 
144  } else if (only_changed(flags, F_new_x | F_new_y)) {
145  // Only change in X, Y
146  _store_stop = false;
147  d_setSmXY(_store_xyz[0], _store_xyz[1]);
148 
149  } else if (only_changed(flags, F_new_x | F_new_z)) {
150  // Only change in X, Z
151  _store_stop = false;
152  d_setSmXZ(_store_xyz[0], _store_xyz[2]);
153 
154  } else if (only_changed(flags, F_new_x | F_new_y | F_new_z)) {
155  // Only change in X, Y, Z
156  _store_stop = false;
157  d_setSmPos(_store_xyz[0], _store_xyz[1], _store_xyz[2]);
158 
159  } else if (only_changed(flags, F_new_h | F_new_p | F_new_r)) {
160  // Only change in H, P, R
161  _store_stop = false;
162  d_setSmHpr(_store_hpr[0], _store_hpr[1], _store_hpr[2]);
163 
164  } else if (only_changed(flags, F_new_x | F_new_y | F_new_h)) {
165  // Only change in X, Y, H
166  _store_stop = false;
167  d_setSmXYH(_store_xyz[0], _store_xyz[1], _store_hpr[0]);
168 
169  } else if (only_changed(flags, F_new_x | F_new_y | F_new_z | F_new_h)) {
170  // Only change in X, Y, Z, H
171  _store_stop = false;
172  d_setSmXYZH(_store_xyz[0], _store_xyz[1], _store_xyz[2], _store_hpr[0]);
173 
174  } else {
175  // Any other change
176  _store_stop = false;
177  d_setSmPosHpr(_store_xyz[0], _store_xyz[1], _store_xyz[2],
178  _store_hpr[0], _store_hpr[1], _store_hpr[2]);
179  }
180 }
181 
182 /**
183  * Examines only X, Y, and H of the pos/hpr information, and broadcasts the
184  * appropriate messages.
185  */
188  LPoint3 xyz = _node_path.get_pos();
189  LVecBase3 hpr = _node_path.get_hpr();
190 
191  int flags = 0;
192 
193  if (!IS_THRESHOLD_EQUAL(_store_xyz[0], xyz[0], smooth_node_epsilon)) {
194  _store_xyz[0] = xyz[0];
195  flags |= F_new_x;
196  }
197 
198  if (!IS_THRESHOLD_EQUAL(_store_xyz[1], xyz[1], smooth_node_epsilon)) {
199  _store_xyz[1] = xyz[1];
200  flags |= F_new_y;
201  }
202 
203  if (!IS_THRESHOLD_EQUAL(_store_hpr[0], hpr[0], smooth_node_epsilon)) {
204  _store_hpr[0] = hpr[0];
205  flags |= F_new_h;
206  }
207 
208  if (flags == 0) {
209  // No change. Send one and only one "stop" message.
210  if (!_store_stop) {
211  _store_stop = true;
212  d_setSmStop();
213  }
214 
215  } else if (only_changed(flags, F_new_h)) {
216  // Only change in H.
217  _store_stop = false;
218  d_setSmH(_store_hpr[0]);
219 
220  } else if (only_changed(flags, F_new_x | F_new_y)) {
221  // Only change in X, Y
222  _store_stop = false;
223  d_setSmXY(_store_xyz[0], _store_xyz[1]);
224 
225  } else {
226  // Any other change.
227  _store_stop = false;
228  d_setSmXYH(_store_xyz[0], _store_xyz[1], _store_hpr[0]);
229  }
230 }
231 
232 /**
233  * Examines only X and Y of the pos/hpr information, and broadcasts the
234  * appropriate messages.
235  */
238  LPoint3 xyz = _node_path.get_pos();
239 
240  int flags = 0;
241 
242  if (!IS_THRESHOLD_EQUAL(_store_xyz[0], xyz[0], smooth_node_epsilon)) {
243  _store_xyz[0] = xyz[0];
244  flags |= F_new_x;
245  }
246 
247  if (!IS_THRESHOLD_EQUAL(_store_xyz[1], xyz[1], smooth_node_epsilon)) {
248  _store_xyz[1] = xyz[1];
249  flags |= F_new_y;
250  }
251 
252  if (flags == 0) {
253  // No change. Send one and only one "stop" message.
254  if (!_store_stop) {
255  _store_stop = true;
256  d_setSmStop();
257  }
258 
259  } else {
260  // Any other change.
261  _store_stop = false;
262  d_setSmXY(_store_xyz[0], _store_xyz[1]);
263  }
264 }
265 
266 /**
267  * Fills up the packer with the data appropriate for sending an update on the
268  * indicated field name, up until the arguments.
269  */
270 void CDistributedSmoothNodeBase::
271 begin_send_update(DCPacker &packer, const std::string &field_name) {
272  DCField *field = _dclass->get_field_by_name(field_name);
273  nassertv(field != nullptr);
274 
275  if (_is_ai) {
276 
277  packer.raw_pack_uint8(1);
278  packer.RAW_PACK_CHANNEL(_do_id);
279  packer.RAW_PACK_CHANNEL(_ai_id);
280  // packer.raw_pack_uint8('A');
281  packer.raw_pack_uint16(STATESERVER_OBJECT_SET_FIELD);
282  packer.raw_pack_uint32(_do_id);
283  packer.raw_pack_uint16(field->get_number());
284 
285  } else {
286  packer.raw_pack_uint16(CLIENT_OBJECT_SET_FIELD);
287  packer.raw_pack_uint32(_do_id);
288  packer.raw_pack_uint16(field->get_number());
289  }
290 
291  packer.begin_pack(field);
292  packer.push();
293 }
294 
295 /**
296  * Appends the timestamp and sends the update.
297  */
298 void CDistributedSmoothNodeBase::
299 finish_send_update(DCPacker &packer) {
300 #ifdef HAVE_PYTHON
301  nassertv(_clock_delta != nullptr);
302  PyObject *clock_delta = PyObject_GetAttrString(_clock_delta, "delta");
303  nassertv(clock_delta != nullptr);
304  double delta = PyFloat_AsDouble(clock_delta);
305  Py_DECREF(clock_delta);
306 #else
307  static const double delta = 0.0f;
308 #endif // HAVE_PYTHON
309 
310  double local_time = ClockObject::get_global_clock()->get_real_time();
311 
312  int network_time = (int)cfloor(((local_time - delta) * network_time_precision) + 0.5);
313  // Preserves the lower NetworkTimeBits of the networkTime value, and extends
314  // the sign bit all the way up.
315  network_time = ((network_time + 0x8000) & 0xFFFF) - 0x8000;
316  packer.pack_int(network_time);
317 
318  packer.pop();
319  bool pack_ok = packer.end_pack();
320  if (pack_ok) {
321  Datagram dg(packer.get_data(), packer.get_length());
322  nassertv(_repository != nullptr);
323  _repository->send_datagram(dg);
324 
325  } else {
326 #ifndef NDEBUG
327  if (packer.had_range_error()) {
328  std::ostringstream error;
329  error << "Node position out of range for DC file: "
330  << _node_path << " pos = " << _store_xyz
331  << " hpr = " << _store_hpr
332  << " zoneId = " << _currL[0];
333 
334 #ifdef HAVE_PYTHON
335  std::string message = error.str();
336  distributed_cat.warning()
337  << message << "\n";
338  PyErr_SetString(PyExc_ValueError, message.c_str());
339 #else
340  nassert_raise(error.str());
341 #endif
342 
343  } else {
344  const char *message = "Unexpected pack error in DC file.";
345 #ifdef HAVE_PYTHON
346  distributed_cat.warning()
347  << message << "\n";
348  PyErr_SetString(PyExc_TypeError, message);
349 #else
350  nassert_raise(message);
351 #endif
352  }
353 #endif
354  }
355 }
356 
357 /**
358  * Appends the timestamp and sends the update.
359  */
361 set_curr_l(uint64_t l) {
362  _currL[1] = l;
363 }
364 
365 void CDistributedSmoothNodeBase::
366 print_curr_l() {
367  std::cout << "printCurrL: sent l: " << _currL[1] << " last set l: " << _currL[0] << "\n";
368 }
static ClockObject * get_global_clock()
Returns a pointer to the global ClockObject.
Definition: clockObject.I:215
bool had_range_error() const
Returns true if there has been an range validation error since the most recent call to begin(); in pa...
Definition: dcPacker.I:553
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void broadcast_pos_hpr_full()
Examines the complete pos/hpr information to see which of the six elements have changed,...
void pack_int(int value)
Packs the indicated numeric or string value into the stream.
Definition: dcPacker.I:153
int get_number() const
Returns a unique index number associated with this field.
Definition: dcField.I:19
bool is_empty() const
Returns true if the NodePath contains no nodes.
Definition: nodePath.I:188
void broadcast_pos_hpr_xyh()
Examines only X, Y, and H of the pos/hpr information, and broadcasts the appropriate messages.
A single field of a Distributed Class, either atomic or molecular.
Definition: dcField.h:37
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void raw_pack_uint8(unsigned int value)
Packs the data into the buffer between packing sessions.
Definition: dcPacker.I:740
bool send_datagram(const Datagram &dg)
Queues the indicated datagram for sending to the server.
void set_curr_l(uint64_t l)
Appends the timestamp and sends the update.
Defines a particular DistributedClass as read from an input .dc file.
Definition: dcClass.h:44
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
size_t get_length() const
Returns the current length of the buffer.
Definition: dcPacker.I:583
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void raw_pack_uint32(unsigned int value)
Packs the data into the buffer between packing sessions.
Definition: dcPacker.I:758
DCField * get_field_by_name(const std::string &name) const
Returns a pointer to the DCField that shares the indicated name.
Definition: dcClass.cxx:201
void raw_pack_uint16(unsigned int value)
Packs the data into the buffer between packing sessions.
Definition: dcPacker.I:749
void push()
Marks the beginning of a nested series of fields.
Definition: dcPacker.cxx:411
const char * get_data() const
Returns the beginning of the data buffer.
Definition: dcPacker.I:641
LVecBase3 get_hpr() const
Retrieves the rotation component of the transform.
Definition: nodePath.cxx:1058
get_real_time
Returns the actual number of seconds elapsed since the ClockObject was created, or since it was last ...
Definition: clockObject.h:92
void send_everything()
Broadcasts the current pos/hpr in its complete form.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
LPoint3 get_pos() const
Retrieves the translation component of the transform.
Definition: nodePath.cxx:992
void broadcast_pos_hpr_xy()
Examines only X and Y of the pos/hpr information, and broadcasts the appropriate messages.
void begin_pack(const DCPackerInterface *root)
Begins a packing session.
Definition: dcPacker.cxx:73
This class can be used for packing a series of numeric and string data into a binary stream,...
Definition: dcPacker.h:34
void initialize(const NodePath &node_path, DCClass *dclass, CHANNEL_TYPE do_id)
Initializes the internal structures from some constructs that are normally stored only in Python.
bool end_pack()
Finishes a packing session.
Definition: dcPacker.cxx:98
An ordered list of data elements, formatted in memory for transmission over a socket or writing to a ...
Definition: datagram.h:38
void pop()
Marks the end of a nested series of fields.
Definition: dcPacker.cxx:495
NodePath is the fundamental system for disambiguating instances, and also provides a higher-level int...
Definition: nodePath.h:161