Example #1
0
        public void RxMsgFromClient(Player.Player player, byte[] msg)
        {
            if (msg[0] == 0x01 && msg.Length == 1)
            {
                // Keepalive
                byte[] omsg = { 0xFF };
                player.SendMsgToClient(omsg);
            }
            else if (msg[0] == 0x01 && msg.Length == 75)
            {
                // Save login information.
                int    pos       = 3;
                string accountid = FLMsgType.GetUnicodeStringLen16(msg, ref pos);

                //string accDirPath = player.Runner.Server.AcctPath + Path.DirectorySeparatorChar +
                //                    FLMsgType.FLNameToFile(accountid);
                // If the account directory does not exist, create it and save the account id file.
                //if (!Directory.Exists(accDirPath))
                //    Directory.CreateDirectory(accDirPath);
                //FLUtility.WriteAccountID(accDirPath, accountid);


                byte[] omsg = { 0x02, 0x02 };
                // If the account is banned kick the player.

                var accs = Old.CharacterDB.Database.GetAccount(accountid);
                //TODO: check if banning works; possibly make separate table for ID bans
                bool isbanned = false;
                if (accs != null)
                {
                    if (accs[0].IsBanned)
                    {
                        isbanned = true;
                    }
                }
                //if (File.Exists(accDirPath + Path.DirectorySeparatorChar + "banned"))
                //{


                //    FLMsgType.AddUInt8(ref omsg, FLMsgType.MSG_TYPE_LOGIN_REPORT_TYPE_BANNED);
                //}
                // If the account is already logged in, reject the login
                // fixme: this is not thread safe
                //else
                if (isbanned)
                {
                    FLMsgType.AddUInt8(ref omsg, FLMsgType.MSG_TYPE_LOGIN_REPORT_TYPE_BANNED);
                }
                else
                if (player.Runner.Server.FindPlayerByAccountID(accountid) != null)
                {
                    FLMsgType.AddUInt8(ref omsg, FLMsgType.MSG_TYPE_LOGIN_REPORT_TYPE_INUSE);
                }
                else
                {
                    FLMsgType.AddUInt8(ref omsg, FLMsgType.MSG_TYPE_LOGIN_REPORT_TYPE_OKAY);
                }
                player.AccountID = accountid;
                player.SendMsgToClient(omsg);
            }
            else if (msg[0] == 0x05 && msg[1] == 0x03)
            {
                // char info request
                player.SaveCharFile();
                player.SetState(DPCSelectingCharacterState.Instance());
            }
            else
            {
                // Unexpected packet. Log and ignore it.
                player.Log.AddLog(LogType.FL_MSG, "Unexpected message: client rx", player.DPSess, msg);
            }
        }
Example #2
0
        public void RxMsgFromClient(Player.Player player, byte[] msg)
        {
            if (msg[0] == 0x01 && msg.Length == 1)
            {
                // Keepalive
                byte[] omsg = { 0xFF };
                player.SendMsgToClient(omsg);
            }
            else if (msg[0] == 0x05 && msg[1] == 0x03)
            {
                // FLPACKET_CLIENT_REQUESTCHARINFO
                player.Log.AddLog(LogType.FL_MSG, "FLPACKET_CLIENT_REQUESTCHARINFO");
                Packets.SendCharInfoRequestResponse(player);
            }
            else if (msg[0] == 0x06 && msg[1] == 0x03)
            {
                // FLPACKET_CLIENT_SELECTCHARACTER
                var nameBefore   = player.Name;
                var pos          = 2;
                var charfilename = FLMsgType.GetAsciiStringLen16(msg, ref pos);

                var acct = Old.CharacterDB.Database.GetOneAccount(player.AccountID, charfilename);

                var result = player.LoadCharFile(acct, player.Log);

                if (result != null)
                {
                    player.Log.AddLog(LogType.ERROR, "error: cannot load character accdir={0} charfile={1} reason={2}",
                                      acct.CharName, result);
                    return;
                }

                player.Log.AddLog(LogType.GENERAL,
                                  "FLPACKET_CLIENT_SELECTCHARACTER charfilename={0} name={1} system={2}", charfilename, player.Name,
                                  player.Ship.System.Nickname);
                if (player.Ship != null && player.Ship.Objid != 0)
                {
                    player.Runner.DelSimObject(player.Ship);
                }

                player.OnCharacterSelected(nameBefore == player.Name, nameBefore == null);
                player.Update();
            }
            else if (msg[0] == 0x39 && msg[1] == 0x03)
            {
                player.Log.AddLog(LogType.FL_MSG, "FLPACKET_CLIENT_CREATENEWCHAR");
                // New character
                int    pos      = 2;
                string charname = FLMsgType.GetUnicodeStringLen16(msg, ref pos);

                //TODO: do nothing when charname exists?
                if (Old.CharacterDB.Database.GetAccount(@"CharName", charname) != null)
                {
                    return;
                }

                Old.CharacterDB.Database.AddAccount(player.AccountID, charname);

                //string charfile = player.Runner.Server.AcctPath +
                //                  Path.DirectorySeparatorChar + FLMsgType.FLNameToFile(player.AccountID) +
                //                  Path.DirectorySeparatorChar + FLMsgType.FLNameToFile(charname) + ".fl";
                //if (!File.Exists(charfile))
                //{
                //    var file =
                //        new FLDataFile(player.Runner.Server.AcctPath + Path.DirectorySeparatorChar + "default.fl", true);
                //    file.AddSetting("Player", "name", new object[] {FLUtility.EncodeUnicodeHex(charname)});
                //    file.SaveSettings(charfile, false);
                //}

                Packets.SendCharInfoRequestResponse(player);
            }
            else if (msg[0] == 0x3a && msg[1] == 0x03)
            {
                player.Log.AddLog(LogType.FL_MSG, "FLPACKET_CLIENT_DESTROYCHAR");

                // Delete character
                var pos      = 2;
                var charfile = FLMsgType.GetAsciiStringLen16(msg, ref pos);

                Old.CharacterDB.Database.DelAccount(player.AccountID, charfile);

                Packets.SendCharInfoRequestResponse(player);
            }
            else
            {
                // Unexpected packet. Log and ignore it.
                player.Log.AddLog(LogType.ERROR, "Unexpected message: client rx", player.DPSess, msg);
            }
        }
Example #3
0
        /// <summary>
        ///     Try to send a dframe or a keep alive if no user data is waiting to be sent.
        /// </summary>
        /// <param name="sess"></param>
        /// <returns>Return true if a dframe was sent</returns>
        public bool SendDFrame(Session sess)
        {
            lock (sess)
            {
                if (sess.UserData.Count > 0)
                {
                    // If the window is full, don't send any thing
                    if (IsAckWindowFull(sess))
                    {
                        return(false);
                    }

                    // If we have sent a user data message that had to be carried in multiple
                    // dframes then stop sending. We can't have more than one of these on the
                    // wire at one time (if I've intepreted the specs correctly).
                    if (sess.MultipleDframePacket)
                    {
                        return(false);
                    }

                    // The retry time should start at 100 + rtt * 2.5 according to the specs but we use
                    // 2 as this is a round number.
                    uint retry_time = 100 + (sess.Rtt * 2);
                    while (sess.UserData.Count > 0)
                    {
                        byte[] ud = sess.UserData.First();
                        sess.UserData.RemoveFirst();

                        // Break the user data block into sizes smaller than the ethernet mtu. We
                        // assume an MTU of 1450 as some infrastructure steals some bytes.
                        int  offset       = 0;
                        bool first_packet = true;
                        bool last_packet  = false;
                        while (offset < ud.Length)
                        {
                            int length;
                            if ((ud.Length - offset) > 1450)
                            {
                                length = 1450;
                                sess.MultipleDframePacket = true;
                            }
                            else
                            {
                                length      = ud.Length - offset;
                                last_packet = true;
                            }

                            byte[] pkt = { 0x07, 0x00, sess.NextTxSeq, sess.NextRxSeq };

                            // If this is the first packet, set the flag to indicate this.
                            if (first_packet)
                            {
                                pkt[0] |= 0x10;
                            }

                            // If this is the last packet, set the flag to indicate this
                            if (last_packet)
                            {
                                pkt[0] |= 0x20;
                            }

                            // If the session isn't fully connected then this must be a session establishment
                            // message
                            if (sess.SessionState == Session.State.CONNECTING_SESSINFO)
                            {
                                pkt[0] |= 0x40;
                            }

                            FLMsgType.AddArray(ref pkt, ud, offset, length);

                            var spkt = new Session.Pkt();
                            spkt.Data      = pkt;
                            spkt.RetryTime = DateTime.UtcNow.AddMilliseconds(retry_time);
                            spkt.SendTime  = DateTime.UtcNow;

                            sess.UserDataPendingAck[sess.NextTxSeq] = spkt;
                            sess.BytesTx += pkt.Length;
                            TxStart(pkt, sess.Client);

                            // Increase the retry times if multiple packets are sent so that
                            // we're less likely to send a massive burst of packets to retry.
                            retry_time += 5;

                            sess.NextTxSeq++;

                            first_packet = false;
                            offset      += length;

                            // fixme: it's possible for a multi-dframe user data message to overrun
                            // the valid seq window size. this is bad and the connection will fail.
                        }

                        // If we have sent a user data message that had to be carried in multiple
                        // dframes then stop sending. We can't have more than one of these on the
                        // wire at one time (if I've intepreted the specs correctly).
                        if (sess.MultipleDframePacket)
                        {
                            break;
                        }

                        // If the window is full, don't send any more
                        if (IsAckWindowFull(sess))
                        {
                            break;
                        }
                    }

                    return(true);
                }
            }

            return(false);
        }
Example #4
0
        /// <summary>
        ///     Send a dummy trans
        /// </summary>
        /// <param name="sess"></param>
        private void SendTUDSessionInfo(Session sess)
        {
            var pkt = new byte[0];

            FLMsgType.AddUInt32(ref pkt, 0xC2);                           // dwPacketType
            FLMsgType.AddUInt32(ref pkt, 0);                              // dwReplyOffset
            FLMsgType.AddUInt32(ref pkt, 0);                              // dwReplySize

            FLMsgType.AddUInt32(ref pkt, 0x50);                           // dwApplicationDescSize
            FLMsgType.AddUInt32(ref pkt, 0x01);                           // dwFlags
            FLMsgType.AddUInt32(ref pkt, max_players + 1);                // dwMaxPlayers
            FLMsgType.AddUInt32(ref pkt, (uint)dplay_sessions.Count + 1); // dwCurrentPlayers
            FLMsgType.AddUInt32(ref pkt, 0x6C + 0x60);                    // dwSessionNameOffset
            FLMsgType.AddUInt32(ref pkt, (uint)server_name.Length * 2);   // dwSessionNameSize
            FLMsgType.AddUInt32(ref pkt, 0);                              // dwPasswordOffset
            FLMsgType.AddUInt32(ref pkt, 0);                              // dwPasswordSize
            FLMsgType.AddUInt32(ref pkt, 0);                              // dwReservedDataOffset
            FLMsgType.AddUInt32(ref pkt, 0);                              // dwReservedDataSize
            FLMsgType.AddUInt32(ref pkt, 0);                              // dwApplicationReservedDataOffset
            FLMsgType.AddUInt32(ref pkt, 0);                              // dwApplicationReservedDataSize
            FLMsgType.AddArray(ref pkt, ApplicationInstanceGUID);
            FLMsgType.AddArray(ref pkt, ApplicationGUID);
            FLMsgType.AddUInt32(ref pkt, sess.DPlayID); // dpnid
            FLMsgType.AddUInt32(ref pkt, sess.DPlayID); // dwVersion
            FLMsgType.AddUInt32(ref pkt, 0);            // dwVersionNotUsed
            FLMsgType.AddUInt32(ref pkt, 2);            // dwEntryCount
            FLMsgType.AddUInt32(ref pkt, 0);            // dwMembershipCount

            // server name table entry
            FLMsgType.AddUInt32(ref pkt, 1);        // dpnid
            FLMsgType.AddUInt32(ref pkt, 0);        // dpnidOwner
            FLMsgType.AddUInt32(ref pkt, 0x000402); // dwFlags
            FLMsgType.AddUInt32(ref pkt, 2);        // dwVersion
            FLMsgType.AddUInt32(ref pkt, 0);        // dwVersionNotUsed
            FLMsgType.AddUInt32(ref pkt, 7);        // dwDNETVersion
            FLMsgType.AddUInt32(ref pkt, 0);        // dwNameOffset
            FLMsgType.AddUInt32(ref pkt, 0);        // dwNameSize
            FLMsgType.AddUInt32(ref pkt, 0);        // dwDataOffset
            FLMsgType.AddUInt32(ref pkt, 0);        // dwDataSize
            FLMsgType.AddUInt32(ref pkt, 0);        // dwURLOffset
            FLMsgType.AddUInt32(ref pkt, 0);        // dwURLSize

            // connecting client name table entry
            FLMsgType.AddUInt32(ref pkt, sess.DPlayID); // dpnid
            FLMsgType.AddUInt32(ref pkt, 0);            // dpnidOwner
            FLMsgType.AddUInt32(ref pkt, 0x020000);     // dwFlags
            FLMsgType.AddUInt32(ref pkt, sess.DPlayID); // dwVersion
            FLMsgType.AddUInt32(ref pkt, 0);            // dwVersionNotUsed
            FLMsgType.AddUInt32(ref pkt, 7);            // dwDNETVersion
            FLMsgType.AddUInt32(ref pkt, 0);            // dwNameOffset
            FLMsgType.AddUInt32(ref pkt, 0);            // dwNameSize
            FLMsgType.AddUInt32(ref pkt, 0);            // dwDataOffset
            FLMsgType.AddUInt32(ref pkt, 0);            // dwDataSize
            FLMsgType.AddUInt32(ref pkt, 0);            // dwURLOffset
            FLMsgType.AddUInt32(ref pkt, 0);            // dwURLSize

            FLMsgType.AddUnicodeStringLen0(ref pkt, server_name);

            sess.UserData.AddLast(pkt);
            SendDFrame(sess);
        }
Example #5
0
        public void ProcessPktFromClient(byte[] pkt, IPEndPoint client)
        {
            log.AddLog(LogType.DPLAY_MSG, "c>s client={0} pkt={1}", client, pkt);

            // If this message is too short, chuck it away
            if (pkt.Length < 2)
            {
                return;
            }

            // If this message is a enum server status then reply to the query. This tricks people
            // into thinking the server has a better ping than it does.
            int  pos = 0;
            uint cmd = FLMsgType.GetUInt8(pkt, ref pos);

            if (cmd == 0x00 && pkt.Length >= 4)
            {
                uint opcode = FLMsgType.GetUInt8(pkt, ref pos);
                if (opcode == 0x02 && pkt.Length >= 4)
                {
                    uint enum_payload = FLMsgType.GetUInt16(pkt, ref pos);
                    SendCmdEnumResponse(client, (ushort)enum_payload);
                }
            }

            // If the data is at least 12 bytes and the first byte is
            // either 0x80 or 0x88 (PACKET_COMMAND_CFRAME or PACKET_COMMAND_CFRAME |
            // PACKET_COMMAND_POLL), it MUST process the message as a CFRAME
            // (section 3.1.5.1) command frame.
            else if ((cmd == 0x80 || cmd == 0x88) && pkt.Length >= 12)
            {
                uint opcode = FLMsgType.GetUInt8(pkt, ref pos);

                // The CONNECT packet is used to request a connection. If accepted, the response
                // is a CONNECTED (section 2.2.1.2) packet
                if (opcode == 0x01)
                {
                    byte msg_id = FLMsgType.GetUInt8(pkt, ref pos);
                    byte rsp_id = FLMsgType.GetUInt8(pkt, ref pos);

                    uint version   = FLMsgType.GetUInt32(pkt, ref pos);
                    uint dplayid   = FLMsgType.GetUInt32(pkt, ref pos);
                    uint timestamp = FLMsgType.GetUInt32(pkt, ref pos);

                    // Create a new session.
                    Session sess = GetSession(client);
                    if (sess == null)
                    {
                        sess         = new Session(client);
                        sess.DPlayID = dplayid;
                        lock (dplay_sessions)
                        {
                            dplay_sessions[client] = sess;
                        }
                    }

                    lock (sess)
                    {
                        // If the session id has changed, assume that the server is wrong
                        // and kill the existing connection and start a new one.
                        // This behaviour differs from the dplay specification.
                        if (sess.DPlayID != 0 && sess.DPlayID != dplayid)
                        {
                            Destroy(sess, "changed dsessid");
                        }

                        // If the session is fully connected because the client has
                        // sent us a connect acknowledge then ignore this.
                        if (sess.SessionState == Session.State.CONNECTED)
                        {
                            return;
                        }

                        // Otherwise this is a new connection. Reset the session information.
                        sess.SessionState     = Session.State.CONNECTING;
                        sess.LastClientRxTime = DateTime.UtcNow;
                        sess.StartTime        = DateTime.Now;

                        sess.Rtt     = 200;
                        sess.LostRx  = 0;
                        sess.BytesRx = 0;
                        sess.LostTx  = 0;
                        sess.BytesTx = 0;

                        sess.NextRxSeq = 0;
                        sess.NextTxSeq = 0;

                        sess.MsgID = 0;
                        sess.OutOfOrder.Clear();
                        sess.UserData.Clear();
                        sess.UserDataPendingAck.Clear();

                        sess.MultipleDframePacket = false;
                        sess.SessionTimer         = new Timer(SessionTimer, sess, 100, 20);

                        SendCmdConnectAccept(sess, msg_id);
                    }
                }
                // Receive a SACK and process it
                else if (opcode == 0x06)
                {
                    byte flags = FLMsgType.GetUInt8(pkt, ref pos);
                    byte retry = FLMsgType.GetUInt8(pkt, ref pos);
                    // The seq field indicates the seq of the next message that the client will send.
                    byte seq = FLMsgType.GetUInt8(pkt, ref pos);
                    // The next_rx field indicates the message seq that the client is waiting to receive
                    byte nrcv = FLMsgType.GetUInt8(pkt, ref pos);
                    pos += 2; // skip padding
                    uint timestamp = FLMsgType.GetUInt32(pkt, ref pos);

                    // Ignore packets for sessions that don't exist
                    Session sess = GetSession(client);
                    if (sess == null)
                    {
                        return;
                    }

                    lock (sess)
                    {
                        sess.LastClientRxTime = DateTime.UtcNow;
                        sess.BytesRx         += pkt.Length;

                        // If the hi sack mask is present, resend any requested packets.
                        if ((flags & 0x02) == 0x02)
                        {
                            uint mask = FLMsgType.GetUInt32(pkt, ref pos);
                            DoRetryOnSACKMask(sess, mask, nrcv);
                        }

                        // If the hi sack mask is present, resend any requested packets.
                        if ((flags & 0x04) == 0x04)
                        {
                            uint mask = FLMsgType.GetUInt32(pkt, ref pos);
                            DoRetryOnSACKMask(sess, mask, (byte)(nrcv + 32));
                        }

                        // At this point bSeq sequence ID is valid, the bNRcv field
                        // is to be inspected. All previously sent TRANS_USERDATA_HEADER packets that
                        // are covered by the bNRcv sequence ID, that is, those packets that had been sent
                        // with bSeq values less than bNRcv (accounting for 8-bit counter wrapping) are
                        // acknowledged. These packets do not have to be remembered any longer, and their
                        // retry timers can be canceled.
                        DoAcknowledgeUserData(sess, nrcv);

                        // Try to send data if there's data waiting to be sent and send a
                        // selective acknowledgement if we didn't sent a dframe and the client
                        // requested an acknowledgement.
                        if (!SendDFrame(sess) && cmd == 0x88)
                        {
                            SendCmdSACK(sess);
                        }
                    }
                }
            }

            // If a packet arrives, the recipient SHOULD first check whether
            // it is large enough to be a minimal data frame (DFRAME) (4 bytes)
            // and whether the first byte has the low bit (PACKET_COMMAND_DATA) set.
            else if ((cmd & 0x01) == 0x01 && pkt.Length >= 4)
            {
                uint control = FLMsgType.GetUInt8(pkt, ref pos);
                byte seq     = FLMsgType.GetUInt8(pkt, ref pos);
                byte nrcv    = FLMsgType.GetUInt8(pkt, ref pos);

                // Ignore packets for sessions that don't exist
                Session sess = GetSession(client);
                if (sess == null)
                {
                    return;
                }

                lock (sess)
                {
                    sess.LastClientRxTime = DateTime.UtcNow;
                    sess.BytesRx         += pkt.Length;

                    // This is a disconnect. We ignore the soft disconnect and immediately
                    // drop the session repeating the disconnect a few times to improve the
                    // probability of it getting through.
                    if ((control & 0x08) == 0x08)
                    {
                        Destroy(sess, "client request");
                        return;
                    }

                    // TRANS_USERDATA_HEADER bSeq field MUST be either the next sequence
                    // ID expected or within 63 packets beyond the ID expected by the receiver.
                    // If the sequence ID is not within this range, the payload MUST be ignored.
                    // In addition, a SACK packet SHOULD be sent indicating the expected sequence ID.
                    if (!InWindow(seq, sess.NextRxSeq))
                    {
                        SendCmdSACK(sess);
                        return;
                    }

                    // If the sequence ID is out of order, but still within 63 packets,
                    // the receiver SHOULD queue the payload until it receives either:
                    // - A delayed or retried transmission of the missing packet or packets,
                    // and can now process the sequence in order.
                    // - A subsequent packet with a send mask indicating that the missing
                    // packet or packets did not use PACKET_COMMAND_RELIABLE and will never
                    // be retried. Therefore, the receiver should advance its sequence as if
                    // it had already received and processed the packets.
                    if (seq != sess.NextRxSeq)
                    {
                        log.AddLog(LogType.DPLAY_MSG,
                                   "c>s out of order pkt received client={0} queuing seq={1:X} next_rx_seq={2:X}",
                                   sess.Client, seq, sess.NextRxSeq);
                        sess.OutOfOrder[seq] = pkt;
                        SendCmdSACK(sess);
                        return;
                    }

                    //Test code to simulate packet loss
                    //if (rand.Next(5) == 1)
                    //{
                    //    log.AddLog(String.Format("c>s: DROPPING THE PACKET NOW {0:X}", seq));
                    //    return;
                    //}

                    // Note if this was a retried dframe.
                    if ((control & 0x01) == 0x01)
                    {
                        sess.LostRx++;
                    }

                    // When one or both of the optional SACK mask 32-bit fields is present, and one
                    // or more bits are set in the fields, the sender is indicating that it received a
                    // packet or packets out of order, presumably due to packet loss. The two 32-bit,
                    // little-endian fields MUST be considered as one 64-bit field, where dwSACKMask1
                    // is the low 32 bits and dwSACKMask2 is the high 32 bits. If either 32-bit field
                    // is not available, the entire contents of the 64-bit field MUST be considered as all 0.

                    // The receiver of a SACK mask SHOULD loop through each bit of the combined 64-bit value
                    // in the ascending order of significance. Each bit corresponds to a sequence ID after
                    // bNRcv. If the bit is set, it indicates that the corresponding packet was received
                    // out of order.

                    // The receiver of a SACK mask SHOULD shorten the retry timer for the first frame of
                    // the window to speed recovery from the packet loss. The recommended duration is
                    // 10 milliseconds. This value can be modified according to application and network
                    // requirements. The receiver MAY also choose to remove the selectively acknowledged
                    // packets from its list to retry.
                    if ((control & 0x10) == 0x10)
                    {
                        uint mask = FLMsgType.GetUInt32(pkt, ref pos);
                        DoRetryOnSACKMask(sess, mask, nrcv);
                    }
                    if ((control & 0x20) == 0x20)
                    {
                        uint mask = FLMsgType.GetUInt32(pkt, ref pos);
                        DoRetryOnSACKMask(sess, mask, (byte)(nrcv + 32));
                    }


                    // When one or both of the optional send mask 32-bit fields is present, and one or
                    // more bits are set the fields, the sender is indicating that it sent a packet or
                    // packets that were not marked as reliable and did not receive an acknowledgement yet.
                    // The two 32-bit, little-endian fields MUST be considered as one 64-bit field, where
                    // dwSendMask1 is the low 32 bits and dwSendMask2 is the high 32 bits. If either 32-bit
                    // field is not available, the entire contents of the 64-bit field MUST be considered
                    // as all 0.

                    // The receiver of a send mask SHOULD loop through each bit of the combined 64-bit
                    // value from the least significant bit to the most significant in little-endian byte
                    // order. Each bit corresponds to a sequence ID prior to bSeq, and if that is the bit
                    // that is set, it indicates that the corresponding packet was not sent reliably and
                    // will not be retried. If the recipient of the send mask had not received the packet
                    // and had not already processed a send mask that identified the sequence ID, it SHOULD
                    // consider the packet as dropped and release its placeholder in the sequence. That is,
                    // any sequential messages that could not be indicated because of the gap in the sequence
                    // where the packet that was not marked as reliable had been SHOULD now be reported to
                    // the upper layer.
                    if ((control & 0x40) == 0x40)
                    {
                        FLMsgType.GetUInt32(pkt, ref pos);
                    }
                    if ((control & 0x80) == 0x80)
                    {
                        FLMsgType.GetUInt32(pkt, ref pos);
                    }
                    // However, freelancer always uses reliable packets and so ignore sendmasks.

                    // At this point, we've received the packet we wanted to. Advance the sequence number count
                    // and process this message.
                    sess.NextRxSeq++;
                    ProcessTransUserData(sess, pkt, pos);

                    // If there are queued out of order packets, try to process these.
                    while (sess.OutOfOrder.ContainsKey(sess.NextRxSeq))
                    {
                        log.AddLog(LogType.DPLAY_MSG, "c>s unqueuing out of order pkt client={0} seq={1:X}", sess.Client,
                                   sess.NextRxSeq);
                        pkt = sess.OutOfOrder[sess.NextRxSeq];
                        sess.OutOfOrder.Remove(sess.NextRxSeq);
                        sess.NextRxSeq++;
                        ProcessTransUserData(sess, pkt, pos); // fixme: pos could be wrong if we received a sack mask
                    }

                    // At this point bSeq sequence ID is valid, the bNRcv field
                    // is to be inspected. All previously sent TRANS_USERDATA_HEADER packets that
                    // are covered by the bNRcv sequence ID, that is, those packets that had been sent
                    // with bSeq values less than bNRcv (accounting for 8-bit counter wrapping) are
                    // acknowledged. These packets do not have to be remembered any longer, and their
                    // retry timers can be canceled.
                    DoAcknowledgeUserData(sess, nrcv);

                    // We always do an immediate acknowledge as bandwidth isn't a particular concern
                    // but fast recovery from lost packets is.
                    if (!SendDFrame(sess))
                    {
                        SendCmdSACK(sess);
                    }
                }
            }
        }
Example #6
0
        // FLPACKET_SERVER_CREATESHIP
        public byte[] BuildCreateShip(Ship.Ship ship)
        {
            Log.AddLog(LogType.FL_MSG, "tx FLPACKET_SERVER_CREATESHIP objid={0}", ship.Objid);

            byte[] omsg = { 0x04, 0x02 };
            FLMsgType.AddUInt32(ref omsg, ship.Objid);
            FLMsgType.AddUInt16(ref omsg, ship.Arch.SmallID);
            FLMsgType.AddUInt32(ref omsg, 0);
            FLMsgType.AddUInt32(ref omsg, ship.player != null ? ship.player.FLPlayerID : 0);
            FLMsgType.AddUInt32(ref omsg, ship.com_body);
            FLMsgType.AddUInt32(ref omsg, ship.com_head);

            FLMsgType.AddUInt8(ref omsg, (uint)ship.Accessories.Count);
            foreach (uint accessory in ship.Accessories)
            {
                FLMsgType.AddUInt32(ref omsg, accessory);
            }

            FLMsgType.AddUInt32(ref omsg, ship.voiceid);

            FLMsgType.AddFloat(ref omsg, (float)ship.Position.x);
            FLMsgType.AddFloat(ref omsg, (float)ship.Position.y);
            FLMsgType.AddFloat(ref omsg, (float)ship.Position.z);

            Quaternion q = Quaternion.MatrixToQuaternion(ship.Orientation);

            FLMsgType.AddInt8(ref omsg, (int)(q.I * 127));
            FLMsgType.AddInt8(ref omsg, (int)(q.J * 127));
            FLMsgType.AddInt8(ref omsg, (int)(q.K * 127));
            FLMsgType.AddInt8(ref omsg, (int)(q.W * 127));

            FLMsgType.AddUInt8(ref omsg, (uint)(ship.Health * 255));

            FLMsgType.AddUInt16(ref omsg, (uint)(ship.Items.Count));
            foreach (ShipItem item in ship.Items.Values)
            {
                byte flag = 0;

                if (item.mounted)
                {
                    flag |= 0x01;
                }

                if (item.mission)
                {
                    flag |= 0x02;
                }

                if (item.count == 1)
                {
                    flag |= 0x80;
                }
                else
                {
                    flag |= 0x04;
                }

                if (item.health == 1.0f)
                {
                    flag |= 0x40;
                }

                if (item.hpname.Length > 0)
                {
                    flag |= 0x10;
                }
                else
                {
                    flag |= 0x20;
                }

                FLMsgType.AddUInt8(ref omsg, flag);

                if (item.count != 1)
                {
                    FLMsgType.AddUInt32(ref omsg, item.count);
                }

                if (item.health != 1.0f)
                {
                    FLMsgType.AddUInt8(ref omsg, (uint)(item.health * 255));
                }

                FLMsgType.AddUInt16(ref omsg, item.arch.SmallID);
                FLMsgType.AddUInt8(ref omsg, item.hpid);

                if (item.hpname.Length > 0)
                {
                    FLMsgType.AddAsciiStringLen8(ref omsg, item.hpname + "\0");
                }
            }

            FLMsgType.AddUInt8(ref omsg, (uint)(ship.cols.Count));
            foreach (CollisionGroup col in ship.cols)
            {
                FLMsgType.AddUInt8(ref omsg, col.id);
                FLMsgType.AddUInt8(ref omsg, (uint)(col.health * col.max_hit_pts * 255));
            }

            FLMsgType.AddUInt8(ref omsg, (ship.player != null) ? 4u : 0u); // flag
            FLMsgType.AddFloat(ref omsg, 0);                               // x
            FLMsgType.AddFloat(ref omsg, 0);                               // y
            FLMsgType.AddFloat(ref omsg, 0);                               // z
            FLMsgType.AddInt8(ref omsg, 0);
            FLMsgType.AddUInt16(ref omsg, 0);                              // dunno?
            FLMsgType.AddUInt8(ref omsg, ship.Rank);

            if (ship.player != null)
            {
                FLMsgType.AddUInt8(ref omsg, ship.player.FLPlayerID);
                FLMsgType.AddUInt16(ref omsg, 0);
                FLMsgType.AddUnicodeStringLen8(ref omsg, ship.player.Name);
            }
            else
            {
                var patrol_name = new FLFormatString(0x3f20);
                patrol_name.AddString(0x3016b);
                patrol_name.AddString(0x4074);
                patrol_name.AddString(0x30401);
                patrol_name.AddNumber(0x09);
                FLMsgType.AddArray(ref omsg, patrol_name.GetBytes());

                var ship_name = new FLFormatString(0x3f21);
                ship_name.AddString(0x301a4);
                ship_name.AddString(0x37bac);
                ship_name.AddString(0x37c2b);
                FLMsgType.AddArray(ref omsg, ship_name.GetBytes());
            }

            // The faction associated with the ship. For player ships this can be
            // -1 but for NPCs it needs to be set to a faction ID or the NPC will
            // not have a name shown in space or in the radar/scanner
            FLMsgType.AddUInt32(ref omsg, ship.faction.FactionID);

            // The reputation with reference to the faction..but it doesn't seem to
            // do much
            FLMsgType.AddInt8(ref omsg, -127);
            return(omsg);
        }