mirror of
https://github.com/panda3d/panda3d.git
synced 2025-09-30 08:44:19 -04:00
add dcPacker.txt document
This commit is contained in:
parent
1d0eb7a8a8
commit
876dd16b39
248
direct/src/doc/dcPacker.txt
Executable file
248
direct/src/doc/dcPacker.txt
Executable file
@ -0,0 +1,248 @@
|
||||
This document introduces the basic use of the DCPacker class, which is
|
||||
available to C++ and Python programs for high-level packing and
|
||||
unpacking of messages into bytestreams for shipping over the network,
|
||||
especially via Panda's DistributedObject system. See also the
|
||||
comments in direct/src/dcparser/dcPacker.h and related source files.
|
||||
|
||||
|
||||
OVERVIEW
|
||||
|
||||
The DCPacker has four modes of operation: pack (sequential write),
|
||||
unpack (sequential read), unpack (random read), and repack (random
|
||||
write).
|
||||
|
||||
To enter one of these four modes, call begin_pack(), begin_unpack(),
|
||||
or begin_repack(). (begin_unpack() is used for both kinds of unpack
|
||||
modes.) Once you have called begin, you can call a series of
|
||||
pack_this() or unpack_that() methods, and then you finish up by
|
||||
calling end_pack(), end_unpack(), or end_repack().
|
||||
|
||||
The return value of the end method will be true to indicate that no
|
||||
errors have occurred during the packing/unpacking process, or false if
|
||||
something went wrong (in which case you should probably disregard the
|
||||
output).
|
||||
|
||||
In general, when packing or unpacking a series of values, you call
|
||||
pack_int(), pack_uint(), pack_double(), or pack_string() (or the
|
||||
corresponding unpack methods) according to what kind of data type you
|
||||
have for each value; it will be coerced into the appropriate data size
|
||||
as indicated by the DC file and written to the output buffer.
|
||||
|
||||
To pack an array or an embedded class, or any element which itself is
|
||||
made up of sub-elements, you must bracket the packs for the
|
||||
sub-elements between calls to push() and pop(). This also applies to
|
||||
the individual elements of a DCField; so to pack all the elements of a
|
||||
field, you would call push(), followed by the appropriate pack() for
|
||||
each element, then pop().
|
||||
|
||||
|
||||
PACK MODE (sequential write)
|
||||
|
||||
Pack mode is used to build up a network message from scratch. Call
|
||||
begin_pack() and pass it the pointer to a DCField object. You must
|
||||
immediately call push() to indicate that you will be packing the
|
||||
individual elements of the field, then make a series of pack calls,
|
||||
one for each element on the field in order, followed by a call to
|
||||
pop(), and finally end_pack().
|
||||
|
||||
You must pack all of the elements of the field, from beginning to
|
||||
end--it is an error to leave out any elements, including the elements
|
||||
on the end.
|
||||
|
||||
If end_pack() returns false, there was an error (see ADDITIONAL NOTES,
|
||||
below). Otherwise, you may call get_data() to get a pointer to the
|
||||
packed data record, and get_length() to get the number of bytes in the
|
||||
record.
|
||||
|
||||
DCField *field = dclass->get_field_by_name("setChat");
|
||||
|
||||
DCPacker packer;
|
||||
packer.begin_pack(field);
|
||||
packer.push();
|
||||
packer.pack_string(chatString);
|
||||
packer.pack_int(0);
|
||||
packer.pop();
|
||||
if (!packer.end_pack()) {
|
||||
cerr << "error occurred while packing.\n";
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy(result, packer.get_data(), packer.get_length());
|
||||
|
||||
|
||||
|
||||
UNPACK MODE (sequential read)
|
||||
|
||||
You can also unpack all the elements of a field, from beginning to
|
||||
end. This is very similar to pack mode, above. Start with a call to
|
||||
begin_unpack() and pass in the existing data record for the field, and
|
||||
the pointer to the DCField itself. Then call push(), followed by the
|
||||
appropriate number and type of unpack calls, followed by pop() and
|
||||
end_unpack().
|
||||
|
||||
As above, you must unpack all fields; it is an error not to unpack the
|
||||
fields on the end. However, it is not an error if there are
|
||||
additional bytes in the data buffer; the assumption is the data buffer
|
||||
may be part of a larger buffer. After end_unpack(), you can call
|
||||
get_num_unpacked_bytes() to determine how many bytes of the buffer
|
||||
were consumed.
|
||||
|
||||
DCField *field = dclass->get_field_by_name("setChat");
|
||||
|
||||
DCPacker packer;
|
||||
packer.begin_unpack(source_buffer, source_size, field);
|
||||
packer.push();
|
||||
string chat = packer.unpack_string();
|
||||
int chatFlags = packer.unpack_int();
|
||||
packer.pop();
|
||||
if (!packer.end_unpack()) {
|
||||
cerr << "error occurred while unpacking.\n";
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
UNPACK MODE (random read)
|
||||
|
||||
You can also unpack just the particular elements that you care about
|
||||
by name, in no particular order. To do this, call seek() for each
|
||||
element you wish to unpack, specifying the name of the element. You
|
||||
can only do this for elements that have been given names in the DC
|
||||
file.
|
||||
|
||||
In this case, it is not necessary to bracket the outer unpack calls
|
||||
with push() and pop() (since you are not walking through all the
|
||||
elements of the field). However, you still need to use push() and
|
||||
pop() to unpack the nested elements of an array that you seek to.
|
||||
|
||||
DCField *field = dclass->get_field_by_name("setChat");
|
||||
|
||||
DCPacker packer;
|
||||
packer.begin_unpack(source_buffer, source_size, field);
|
||||
packer.seek("chat");
|
||||
string chat = packer.unpack_string();
|
||||
if (!packer.end_unpack()) {
|
||||
cerr << "error occurred while unpacking.\n";
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
REPACK MODE (random write)
|
||||
|
||||
Repack mode allows you to modify some elements of a previously-packed
|
||||
field, without disturbing the elements you don't specify.
|
||||
begin_repack() takes the same parameters as begin_unpack(), then call
|
||||
seek() for each field you want to modify followed by the appropriate
|
||||
pack call.
|
||||
|
||||
After end_repack() returns true, you can retrieve the newly-repacked
|
||||
field with get_data() and get_length(), just as in pack mode.
|
||||
|
||||
DCField *field = dclass->get_field_by_name("setChat");
|
||||
|
||||
DCPacker packer;
|
||||
packer.begin_repack(source_buffer, source_size, field);
|
||||
packer.seek("chat");
|
||||
packer.pack_string(chatString);
|
||||
if (!packer.end_repack()) {
|
||||
cerr << "error occurred while repacking.\n";
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy(result, packer.get_data(), packer.get_length());
|
||||
|
||||
|
||||
|
||||
ADDITIONAL NOTES
|
||||
|
||||
It is acceptable to call pack_int() for a uint type element and
|
||||
vice-versa; the data type will be range-checked and converted to the
|
||||
appropriate signedness. In general, all of the numeric types are
|
||||
interchangeable--just call the appropriate one according to the data
|
||||
type you already have; don't worry about matching to the data type
|
||||
defined in the DC file. However, if you are trying to write a general
|
||||
algorithm and you need a hint, you can call get_pack_type() to return
|
||||
a suggested type for the next pack call; this will return one of
|
||||
PT_int, PT_uint, PT_double, PT_string, etc.
|
||||
|
||||
The same is true when unpacking: unpack_int() or unpack_uint() may be
|
||||
used interchangeably on signed or unsigned data (but if you call
|
||||
unpack_uint() and the data in the record happens to be negative, you
|
||||
will trigger a pack error). As above, get_pack_type() may be called
|
||||
to return the suggested type for the next unpack call.
|
||||
|
||||
|
||||
If end_pack() or end_repack() returns false, there are two possible
|
||||
causes. (1) You tried to pack some value that exceeded the range
|
||||
specified in the DC file (or the limits of the datatype). In this
|
||||
case, had_range_error() will return true. (2) There was some other,
|
||||
more serious error while packing the data, such as a mismatched type
|
||||
(e.g. pack_string() where a uint16 was expected), or you did not pack
|
||||
the right number of elements. In this case, had_pack_error() will
|
||||
return true. It might be the case that both error flags are
|
||||
triggered.
|
||||
|
||||
If end_unpack() returns false, there are two similar causes. (1)
|
||||
There was an invalid value in the record that exceeded the limits
|
||||
specified in the DC file. This will be indicated by
|
||||
had_range_error(). (2) Some mismatched data type (unpack_string() for
|
||||
a uint16) or the wrong number of elements. This is indicated by
|
||||
had_pack_error(). Note that specifying a too-small return value
|
||||
(e.g. unpack_uint() to retrieve a signed value, or unpack_int() to
|
||||
retrieve a float64 or int64 value greater than 2^32) is considered a
|
||||
pack error, not a range error.
|
||||
|
||||
|
||||
You may call pack_literal_value() for any element for which you want
|
||||
to supply a pre-packed data value (for instance, a default value
|
||||
returned by DCAtomicField::get_element_default()). This will be
|
||||
accepted without further validation. Similarly,
|
||||
unpack_literal_value() will return a string corresponding to the
|
||||
pre-packed value of the current element. Both of these work for
|
||||
composite elements as well as for single-component elements (that is,
|
||||
you may call unpack_literal_value() instead of calling push()
|
||||
.. unpack .. pop() to retrieve an entire pre-packed array in one
|
||||
string).
|
||||
|
||||
Python programmers may be especially interested in pack_object() and
|
||||
unpack_object(). pack_object() will accept any Python object and call
|
||||
the appropriate pack function for it. Python tuple or list will
|
||||
implicitly call push(), followed by pack_object() for all the elements
|
||||
in the list, followed by pop(), so pack_object() can pack deeply
|
||||
nested structures with a single call, and with no need to call push()
|
||||
and pop() explicitly. Conversely, unpack_object() will unpack a
|
||||
deeply nested structure and return an appropriate Python tuple or list
|
||||
or other object. You may also consider DCField::pack_args() and
|
||||
DCField::unpack_args(), which automatically invokes the DCPacker for
|
||||
you.
|
||||
|
||||
You may also find parse_and_pack() and unpack_and_format() useful for
|
||||
presenting data to (and accepting data from) a human user.
|
||||
parse_and_pack() accepts a string formatted in the DC file syntax
|
||||
(that is, with the same syntax accepted for a DC file default value),
|
||||
and packs that value for the current element. It may be a single
|
||||
value or a deeply nested value, with brackets and braces embedded as
|
||||
appropriate. Similarly, unpack_and_format() will unpack a single
|
||||
value or a deeply nested value into the same formatted string. As
|
||||
with pack_object() and unpack_object(), these methods are also
|
||||
implemented on the DCField class for convenience, as parse_string()
|
||||
and format_data().
|
||||
|
||||
|
||||
RANGE VALIDATION
|
||||
|
||||
The DCPacker automatically verifies that all data passing through its
|
||||
fundamental pack or unpack methods fits within the ranges (if any)
|
||||
specified in the DC file for each data type. Violating a range
|
||||
restriction triggers a range error, which is indicated by a false
|
||||
return value from end_pack() / end_unpack() / end_repack() and by a
|
||||
true return value from had_range_error().
|
||||
|
||||
If you just want to verify that a message contains legal values
|
||||
without otherwise inspecting the values, you can use unpack_validate()
|
||||
for this purpose. Since unpack_validate() will work on deeply nested
|
||||
structures, you can just call it once in lieu of the entire push()
|
||||
.. pack .. pop() loop. Furthermore, as in unpack_object() and
|
||||
unpack_and_format(), above, there is a convenience function for this
|
||||
on DCField; just call DCField::validate_ranges() to ensure that the
|
||||
data in the record for the given field fits within its specified
|
||||
limits.
|
Loading…
x
Reference in New Issue
Block a user