Assertion Error for datagram iterator

I’m trying to make a datagram iterator so I can check for my issue with the distributed network system. But…

  File "/usr/share/panda3d/direct/distributed/ConnectionRepository.py", line 498, in connect
    successCallback(*successArgs)
  File "/home/croxis/src/ITF/ITFClientRepository.py", line 194, in connectSuccess
    self.setInterestZones([1])
  File "/usr/share/panda3d/direct/distributed/ClientRepository.py", line 258, in setInterestZones
    self.send(datagram)
  File "/usr/share/panda3d/direct/distributed/ConnectionRepository.py", line 648, in send
    self.sendDatagram(datagram)
AssertionError: _current_index + sizeof(tempvar) <= _datagram->get_length() at line 203 of built/include/datagramIterator.I
bool CConnectionRepository::
send_datagram(const Datagram &dg) {
  ReMutexHolder holder(_lock);

  if (_simulated_disconnect) {
    distributed_cat.warning()
      << "Unable to send datagram during simulated disconnect.\n";
    return false;
  }

  if (get_verbose()) {
    describe_message(nout, "SEND", dg);
  }

  if (is_bundling_messages() && get_want_message_bundling()) {
    bundle_msg(dg);
    return true;
  }

#ifdef WANT_NATIVE_NET
  if(_native)
    return _bdc.SendMessage(dg);
#endif

#ifdef HAVE_NET
  if (_net_conn) {
    _cw.send(dg, _net_conn);
    //
    // MY CODE HERE
    //
    _di = DatagramIterator(_dg);
    _msg_type = _di.get_uint16();
    cout << _msg_type;
    cout << "\n";
    return true;
  }
#endif  // HAVE_NET

etc etc....

You are unpacking the wrong datagram. Replace:

_di = DatagramIterator(_dg); 

with:

_di = DatagramIterator(dg); 

David

Doh! So I managed to get the DoId: 3906 every time I run it. Any pointers (I made a pun!) on getting additional details to find the culprit at the c or python level? I recall finding a function to get a DO from the doid, but i don’t remember where I found it.

Put the line:

nassertr(false, false);

when you detect that ID. That will raise a Python exception at the current line; that may give a hint.

If the Python line isn’t useful, you’ll have to look at the C++ stack; you can get this by putting:

assert-abort 1

in your Config.prc file, which will make the above line generate a fatal C++ error instead of a Python exception. Then you can inspect the C++ stack using the MSVS debugger.

David

File "/home/croxis/src/ITF/ITFAIRepository.py", line 116, in addNewShip
    distributedShip.startPosHprBroadcast()
  File "/home/croxis/src/ITF/DistributedSmoothNodeBase.py", line 95, in startPosHprBroadcast
    self.cnode.sendEverything()
AssertionError: false at line 472 of direct/src/distributed/cConnectionRepository.cxx

cDistributedSmoothNodeBase send_everything

void CDistributedSmoothNodeBase::
send_everything() {
  _currL[0] = _currL[1];
  d_setSmPosHprL(_store_xyz[0], _store_xyz[1], _store_xyz[2], 
                 _store_hpr[0], _store_hpr[1], _store_hpr[2], _currL[0]);
}

Hmm, is this happening from the AI client? What happens if you change the line in DistributedSmoothNodeAI.generate() from:

self.cnode.setRepository(self.air, 1, self.air.ourChannel)

to:

self.cnode.setRepository(self.air, 0, self.air.ourChannel)

?

David

Correct. The DSmoothNode is created on a normal client, I am not sure if that is important or not.

After the change there is no more mysterious message type, but now ignoring update due to not being the owner.

Right, you aren’t the owner, so that is the correct behavior. If you want to allow the AI to move around an object that it didn’t create, you will have to add the “clsend” tag to all of the setComponentL, X, Y, Z, etc. functions for DistributedSmoothNode in the dc file.

David

I think I figured out the problem.

Culprit: cDistributedNodeBase.cxx

Function: begin_send_update.

void CDistributedSmoothNodeBase::
begin_send_update(DCPacker &packer, const string &field_name) {
  DCField *field = _dclass->get_field_by_name(field_name);
  nassertv(field != (DCField *)NULL);

  if (_is_ai) {

    packer.raw_pack_uint8(1);
    packer.RAW_PACK_CHANNEL(_do_id);
    packer.RAW_PACK_CHANNEL(_ai_id);
    //packer.raw_pack_uint8('A');
    packer.raw_pack_uint16(STATESERVER_OBJECT_UPDATE_FIELD);
    packer.raw_pack_uint32(_do_id);
    packer.raw_pack_uint16(field->get_number());

  } else {
    packer.raw_pack_uint16(CLIENT_OBJECT_UPDATE_FIELD);
    packer.raw_pack_uint32(_do_id);
    packer.raw_pack_uint16(field->get_number());
  }

  packer.begin_pack(field);
  packer.push();
}

Normally poshpr changes are made on normal clients so the code in the else segment is run, no problems. However if the AI flag is set to true the first chunk of the if statement is run instead, which seems to of gone untested until me and my shenanigans.

Please correct me if I am wrong, but my impression from the videos and the previous forums posts is that I need to code how to unpack a datagram as there is no magic way to figure out how it is all bundled up.

If this is true I find it unusual that, for the _is_ai block, a unit8 and two raw pack channels are packed BEFORE the unint16 updatefield type as opposed to after it. There is no guarantee that _do_id and _ai_id will be consistent to try and add a check for it in the Server Repository.

The problem is that the cmu server will always unpack the first thing as a uint16, which is not the case when the update is sent as an AI.

While I will probably end up using your workaround, it does seem kind of hackish and dances around the attempt to implement an open source OTP server.

Two questions in an attempt to find a more elegant solution.

  1. Is it possible to release and incorporate the code for this specific element from OTP into the ServerRepository? (My guess is no as I am sure there are a bazillion legal and political issues involved.)

  2. Any suggestions on how it could be handled in the ServerRepository? The only solution I have been able to come up with so far is create a second DataGramItereator instance and check if Uint8 is 1. I foresee problems with this as it could be that a separate datagram message could coincidentally begin with a uint8 that is 1 and result in an improperly interpreted message.

Have you looked into creating your own keyword?

A for instance:
I have the hpr, pos as broadcast clsend. I know this opens it up to hacks but it’s a temp solution and it works.
I have game logic methods sent by the client via GUI elements and tagged with p2p ownsend. They are received by the AI’s version of the doId and handled via local methods and local classes until there’s output to send back. That output is sent with either p2p ownsend (if it’s doId specific) or broadcast if I want others to see it (for instance combat chat). Works as well.

What I want is to create a new keyword: aisend. This is a field that can only be sent by the ai repository. Also want to get airecv working so it only sends those fields to the ai repo’s version of the doId.

I may be wrong in this but I don’t think there’s any other way to dictate via the .dc system who is allowed to send out the message. If it’s ownsend then the doId’s have to match up but it doesn’t stop a client Repo from sending out a field as a doId that you might want the AI to handle explicitly. If it’s clsend then anyone can send it.

Anyway the point being, if you had something such as aisend (which implies broadcast from the ai only), then you wouldn’t need to worry about other clients hacking pos and hpr for godspeed or messing with your ai-run entities. Because the SR would ignore any field that came in with those tags from a repo != ai.

Drwr or someone else can probably point out my faults (and there are many, I am sure). But it’s the only logical solution I came up with without having to go in and mess with the source.

However, I have a few more threads to kill and integrate into my polling dict functions on the ai repo before I get a chance to play with it.

Good luck!

P.S. other thing you could do is have the AI actually create all the DOs, thus they are the owner of everything. I do this for some things in my project (pets, charming, etc.) and it works but I have an additional field to toggle what can and can’t be controlled by a cliennt which is held by a local attribute in an AI’s local class (so nothing is distributed).

The problem is DistributedSmoothNode, with automatic position smoothing, does some unique stuff in a task to help minimize the effects of laggy updates. This is something I would like to use and try to fix.

No, the whole thing about the _is_ai block is that it is completely wrong, at least for the CMU release, which doesn’t make a distinction between AI clients and other clients.

The _is_ai block is only correct for the Disney version, which does have a completely different format for these two types of clients. But this just means that really, this code doesn’t belong in DistributedSmoothNode; it should be elsewhere.

Since it is here, though, you can easily work around that mistake just by telling it you’re not an AI. That’s not a hack; that’s correct information: you’re not a Disney AI. The only reason for the _is_ai switch is to tell the node to format its messages according to the convention for Disney AI’s, which you don’t want it to do.

David

places foot in mouth

Sorry for the word choice, I just got so focused on finding a solution and trying to be helpful.

clsend it is!