public void processRegularStanza( Packet packet ) throws UnauthorizedException, PacketException
    {
        // Name of the room that the stanza is addressed to.
        final String roomName = packet.getTo().getNode();

        if ( roomName == null )
        {
            // Packets to the groupchat service (as opposed to a specific room on the service). This should not occur
            // (should be handled by MultiUserChatServiceImpl instead).
            Log.warn(LocaleUtils.getLocalizedString("muc.error.not-supported") + " " + packet);
            if ( packet instanceof IQ && ((IQ) packet).isRequest() )
            {
                sendErrorPacket(packet, PacketError.Condition.feature_not_implemented, "Unable to process stanza.");
            }
            return;
        }

        StanzaIDUtil.ensureUniqueAndStableStanzaID(packet, packet.getTo().asBareJID());

        final Lock lock = getChatRoomLock(roomName);
        lock.lock();
        try {
            // Get the room, if one exists.
            @Nullable MUCRoom room = getChatRoom(roomName);

            // Determine if this user has a pre-existing occupant data in the addressed room.
            final MUCOccupant preExistingOccupantData;
            if (room == null) {
                preExistingOccupantData = null;
            } else {
                preExistingOccupantData = room.getOccupantByFullJID(packet.getFrom());
            }
            Log.debug("Preexisting occupant data for user {} in room {} (that currently {} exist): {}", packet.getFrom(), roomName, room == null ? "does not" : "does", preExistingOccupantData == null ? "(none)" : preExistingOccupantData);

            if ( packet instanceof IQ )
            {
                process((IQ) packet, room, preExistingOccupantData);
            }
            else {
                // See if it's possible to add an occupant-id (XEP-0421). Add the occupant-id not to the original packet
                // instance (as that might unexpectedly become visible 'by reference' in unrelated code). Even when not
                // adding an occupant-ID, generate a copy of the packet, for consistency.
                final Packet modified = packet.createCopy();
                if (preExistingOccupantData != null) {
                    final List<Element> oldOccupantIds = modified.getElement().elements(QName.get("occupant-id", "urn:xmpp:occupant-id:0"));
                    oldOccupantIds.forEach(oldOccupantId -> modified.getElement().remove(oldOccupantId));

                    modified.getElement().addElement("occupant-id", "urn:xmpp:occupant-id:0").addAttribute("id", preExistingOccupantData.getOccupantId());
                }

                if ( modified instanceof Message )
                {
                    process((Message) modified, room, preExistingOccupantData);
                }
                else if ( modified instanceof Presence )
                {
                    // Return value is non-null while argument is, in case this is a request to create a new room.
                    room = process((Presence) modified, roomName, room, preExistingOccupantData);
                }
            }

            // Ensure that other cluster nodes see any changes that might have been applied.
            if (room != null) {
                syncChatRoom(room);
            }
        } finally {
            lock.unlock();
        }
    }
This method does all stanza routing in the chat server for 'regular' MUC stanzas. Packet routing is actually very
simple:

<ul>
  <li>Discover the room the user is talking to</li>
  <li>If the room is not registered and this is a presence "available" packet, try to join the room</li>
  <li>If the room is registered, and presence "unavailable" leave the room</li>
  <li>Otherwise, rewrite the sender address and send to the room.</li>
</ul>

@param packet The stanza to route
xmppserver/src/main/java/org/jivesoftware/openfire/muc/spi/MultiUserChatServiceImpl.java