/// <summary> /// Reply to a enum query from freelancer clients with our server information. /// </summary> /// <param name="client"></param> /// <param name="enum_payload"></param> public void SendCmdEnumResponse(IPEndPoint client, ushort enum_payload) { // TODO: we could need it const bool password = false; const bool nodpnsvr = true; var application_data = new byte[0]; FLMsgType.AddAsciiStringLen0(ref application_data, "1:1:" + server_version + ":-1910309061:" + server_id + ":"); FLMsgType.AddUnicodeStringLen0(ref application_data, server_description); byte[] pkt = { 0x00, 0x03 }; FLMsgType.AddUInt16(ref pkt, enum_payload); FLMsgType.AddUInt32(ref pkt, 0x58 + (uint)server_name.Length * 2); // ReplyOffset FLMsgType.AddUInt32(ref pkt, (uint)application_data.Length); // ReplySize/ResponseSize FLMsgType.AddUInt32(ref pkt, 0x50); // ApplicationDescSize FLMsgType.AddUInt32(ref pkt, (password ? 0x80u : 0x00u) | (nodpnsvr ? 0x40u : 0x00u)); // ApplicationDescFlags FLMsgType.AddUInt32(ref pkt, max_players + 1); // MaxPlayers FLMsgType.AddUInt32(ref pkt, (uint)dplay_sessions.Count + 1); // CurrentPlayers FLMsgType.AddUInt32(ref pkt, 0x58); // SessionNameOffset FLMsgType.AddUInt32(ref pkt, (uint)server_name.Length * 2); // SessionNameSize FLMsgType.AddUInt32(ref pkt, 0); // PasswordOffset FLMsgType.AddUInt32(ref pkt, 0); // PasswordSize FLMsgType.AddUInt32(ref pkt, 0); // ReservedDataOffset FLMsgType.AddUInt32(ref pkt, 0); // ReservedDataSize FLMsgType.AddUInt32(ref pkt, 0); // ApplicationReservedDataOffset FLMsgType.AddUInt32(ref pkt, 0); // ApplicationReservedDataSize FLMsgType.AddArray(ref pkt, ApplicationInstanceGUID); // ApplicationInstanceGUID FLMsgType.AddArray(ref pkt, ApplicationGUID); // ApplicationGUID FLMsgType.AddUnicodeStringLen0(ref pkt, server_name); // SessionName FLMsgType.AddArray(ref pkt, application_data); // ApplicationData TxStart(pkt, client); }
/// <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); }
/// <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); }
// 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); }