/// <summary> /// Session init; ban and multilogin checks /// </summary> /// <param name="message"></param> private void LoginState(object message) { var msg = (byte[])message; if (msg[0] == 0x01 && msg.Length == 1) { // Keepalive //byte[] omsg = { 0xFF }; Context.Sender.Tell(new byte[] { 0xFF }); } else if (msg[0] == 0x01 && msg.Length == 75) { // Save login information. var pos = 3; var accountid = FLMsgType.GetUnicodeStringLen16(msg, ref pos); byte[] omsg = { 0x02, 0x02 }; // // If the account is banned kick the player. _accountID = accountid; var accs = Database.GetAccount(accountid); // //TODO: check if banning works; possibly make separate table for ID bans var isbanned = false; if (accs != null) { if (accs[0].IsBanned) { isbanned = true; } } // // If the account is already logged in or banned, reject the login var findAccID = Context.ActorSelection("user/server/player/*").Ask <bool>(new PlayerActor.CheckAccountID(accountid), TimeSpan.FromMilliseconds(100)); //findAccID.Wait(250); var accOnline = false; //TODO: heeeavy try { if (findAccID.Result) { accOnline = true; } } catch { } if (isbanned) { //TODO: send and close session FLMsgType.AddUInt8(ref omsg, FLMsgType.MSG_TYPE_LOGIN_REPORT_TYPE_BANNED); } else if (accOnline) { FLMsgType.AddUInt8(ref omsg, FLMsgType.MSG_TYPE_LOGIN_REPORT_TYPE_INUSE); } else { FLMsgType.AddUInt8(ref omsg, FLMsgType.MSG_TYPE_LOGIN_REPORT_TYPE_OKAY); } Context.Parent.Tell(new PlayerActor.SetAccountID(accountid, _flPlayerID)); Context.Sender.Tell(omsg); } else if (msg[0] == 0x05 && msg[1] == 0x03) { // char info request //player.SaveCharFile(); SendCharInfoResponse(); Context.Become(SelectCharState, false); _log.Debug("New state id {0} select-char", _flPlayerID); } else { // Unexpected packet. Log and ignore it. _log.Warn("Unexpected message from FLID {0} in LoginState: {1}", _flPlayerID, msg); //player.Log.AddLog(LogType.FL_MSG, "Unexpected message: client rx", player.DPSess, msg); } }
/// <summary> /// Character selection state. /// </summary> /// <param name="message"></param> private void SelectCharState(object message) { var msg = (byte[])message; if (msg[0] == 0x01 && msg.Length == 1) { // Keepalive //byte[] omsg = { 0xFF }; Context.Sender.Tell(new byte[] { 0xFF }); } else if (msg[0] == 0x05 && msg[1] == 0x03) { // FLPACKET_CLIENT_REQUESTCHARINFO //player.Log.AddLog(LogType.FL_MSG, "FLPACKET_CLIENT_REQUESTCHARINFO"); SendCharInfoResponse(); } else if (msg[0] == 0x06 && msg[1] == 0x03) { //TODO: SelectCharacter // FLPACKET_CLIENT_SELECTCHARACTER //var nameBefore = player.Name; var pos = 2; var charfilename = FLMsgType.GetAsciiStringLen16(msg, ref pos); var acct = Database.GetOneAccount(_accountID, charfilename); //var result = player.LoadCharFile(acct, player.Log); if (acct == null) { _log.Error("Cannot load character id={0} name={1}", _accountID, charfilename); 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); _log.Debug("Char selected: {0}", _accountID); Context.Parent.Tell(new PlayerActor.CharSelected(acct, _flPlayerID), Context.Sender); _baseRef = Context.ActorOf <BaseState>("base"); SendVisitedState(acct.Visits); SendMissionLog(); SendInterfaceState(); SendInitSetReputation(); SendCharSelectVerified(); //SendMiscObjUpdate UNK 2 //TODO: figure out what that means byte[] omsg = { 0x54, 0x02, 0x28, 0x00 }; FLMsgType.AddUInt32(ref omsg, 0); FLMsgType.AddInt32(ref omsg, -1); // faction? Sender.Tell(omsg); //do we really need enum it again? SendCharInfoResponse(); //TODO: infocard update //if ((Runner.Server.IntroMsg != null) && firstLogin) //{ // Packets.SendInfocardUpdate(this, 500000, "Welcome to Discovery"); // string intro = Runner.Server.IntroMsg.Replace("$$player$$", Name); // Packets.SendInfocardUpdate(this, 500001, intro); // Packets.SendPopupDialog(this, new FLFormatString(500000), new FLFormatString(500001), // PopupDialogButtons.POPUPDIALOG_BUTTONS_CENTER_OK); //} Context.ActorSelection("../chat").Tell(new ConsoleMessage(ServerUtils.WelcomeMessage.Replace(@"$$player$$", acct.CharName)), Context.Sender); Become(InBaseState, false); //player.Update(); } else if (msg[0] == 0x39 && msg[1] == 0x03) { //player.Log.AddLog(LogType.FL_MSG, "FLPACKET_CLIENT_CREATENEWCHAR"); // New character var pos = 2; var charname = FLMsgType.GetUnicodeStringLen16(msg, ref pos); //TODO: do nothing when charname exists? //BUG: client hangs if (Database.CheckIfNameOccupied(charname)) { return; } Database.AddAccount(_accountID, charname); SendCharInfoResponse(); } 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); Database.DelAccount(_accountID, charfile); SendCharInfoResponse(); } else { // Unexpected packet. Log and ignore it. _log.Warn("Unexpected message from FLID {0} in SelectCharState: {1}", _flPlayerID, BitConverter.ToString(msg)); } }
/// <summary> /// Sends character list for current account. /// </summary> private void SendCharInfoResponse() { var accs = Database.GetAccount(_accountID); byte[] omsg = { 0x03, 0x02 }; FLMsgType.AddUInt8(ref omsg, 0); // chars if (accs != null) { foreach (var acct in accs) { //try //{ FLMsgType.AddAsciiStringLen16(ref omsg, FLMsgType.FLNameToFile(acct.CharName)); FLMsgType.AddUInt16(ref omsg, 0); FLMsgType.AddUnicodeStringLen16(ref omsg, acct.CharName); FLMsgType.AddUnicodeStringLen16(ref omsg, ""); FLMsgType.AddUInt32(ref omsg, 0); FLMsgType.AddUInt32(ref omsg, 0); FLMsgType.AddUInt32(ref omsg, 0); FLMsgType.AddUInt32(ref omsg, acct.Ship); if (acct.Money > 2000000000) { FLMsgType.AddInt32(ref omsg, 2000000000); } else { FLMsgType.AddInt32(ref omsg, acct.Money); } //TODO: ideally we should check if base, system and equip exists FLMsgType.AddUInt32(ref omsg, FLUtility.CreateID(acct.System)); if (acct.ShipState.Base != null) { FLMsgType.AddUInt32(ref omsg, FLUtility.CreateID(acct.ShipState.Base)); } else { FLMsgType.AddUInt32(ref omsg, 0); } FLMsgType.AddUInt32(ref omsg, 0); FLMsgType.AddUInt32(ref omsg, FLUtility.CreateID(acct.Appearance.Voice)); FLMsgType.AddUInt32(ref omsg, acct.Rank); FLMsgType.AddUInt32(ref omsg, 0); FLMsgType.AddFloat(ref omsg, acct.ShipState.Hull); FLMsgType.AddUInt32(ref omsg, 0); FLMsgType.AddUInt32(ref omsg, 0); FLMsgType.AddUInt32(ref omsg, 0); FLMsgType.AddUInt8(ref omsg, 1); FLMsgType.AddUInt32(ref omsg, acct.Appearance.Body); FLMsgType.AddUInt32(ref omsg, acct.Appearance.Head); FLMsgType.AddUInt32(ref omsg, acct.Appearance.LeftHand); FLMsgType.AddUInt32(ref omsg, acct.Appearance.RightHand); FLMsgType.AddUInt32(ref omsg, 0); FLMsgType.AddUInt8(ref omsg, 1); FLMsgType.AddUInt32(ref omsg, acct.Appearance.Body); FLMsgType.AddUInt32(ref omsg, acct.Appearance.Head); FLMsgType.AddUInt32(ref omsg, acct.Appearance.LeftHand); FLMsgType.AddUInt32(ref omsg, acct.Appearance.RightHand); FLMsgType.AddUInt32(ref omsg, 0); FLMsgType.AddUInt8(ref omsg, (uint)acct.Equipment.Count); uint hpid = 2; foreach (var item in acct.Equipment) { //item.count? FLMsgType.AddUInt32(ref omsg, 1); FLMsgType.AddFloat(ref omsg, item.Health); FLMsgType.AddUInt32(ref omsg, item.Arch); FLMsgType.AddUInt16(ref omsg, hpid); hpid++; //item.mounted FLMsgType.AddUInt16(ref omsg, (1u)); if (item.HpName != "") { FLMsgType.AddAsciiStringLen16(ref omsg, item.HpName + "\0"); } else { FLMsgType.AddAsciiStringLen16(ref omsg, "BAY\0"); } } FLMsgType.AddUInt32(ref omsg, 0); omsg[2]++; //} //catch (Exception e) //{ // _log.Error("Corrupt character when processing charinforequest '{0}'", // e.Message); //} } } FLMsgType.AddUInt32(ref omsg, 0); Context.Sender.Tell(omsg); }
/// <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)_dplaySessions.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); }
/// <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); }
public void InBaseState(object message) { //TODO: handle discon in base state, do save and gracefully exit //for InSpaceState we'll need to remove SimObj and/or //add some kind of timer so player won't F1 into nowhere //keep in mind, client reports awfully big position coords before discon var msg = (byte[])message; if (msg[0] == 0x01 && msg.Length == 1) { // Keepalive //byte[] omsg = { 0xFF }; Context.Sender.Tell(new byte[] { 0xFF }); } else if (msg.Length >= 2) { int type = msg[0] << 8 | msg[1]; switch (type) { case 0x0101: //RxCommonUpdateObject(player, msg); break; case 0x0201: //RxCommonFireWeapon(player, msg); break; case 0x0401: //RxSetTarget(player, msg); break; case 0x0501: //RxChat(player, msg); break; case 0x0801: //RxActivateEquip(player, msg); break; case 0x0E01: //RxActivateCruise(player, msg); break; case 0x0F01: //RxGoTradelane(player, msg); break; case 0x1001: //RxStopTradelane(player, msg); break; case 0x1101: //RxSetWeaponsGroup(player, msg); break; case 0x1301: //RxSetVisitedState(player, msg); break; case 0x1401: //RxJettisionCargo(player, msg); break; case 0x1501: //RxActivateThrusters(player, msg); break; case 0x1601: //RxRequestBestPath(player, msg); break; case 0x1701: //RxRequestNavMap(player, msg); break; case 0x1801: Context.Parent.Tell(new PlayerStatsRequest()); //RxRequestPlayerStats(player, msg); break; case 0x1A01: //RxRequestRank //FLPACKET_COMMON_REQUEST_RANK_LEVEL //answer: 1A 01 04 00 00 00 FF FF FF FF _baseRef.Tell(new RankRequest(), Context.Sender); break; case 0x1c01: //RxSetInterfaceState(player, msg); break; case 0x0303: //RxMunitionCollision(player, msg); break; case 0x0403: //RxRequestLaunch(player, msg); break; case 0x0503: //RxRequestCharInfo(player, msg); break; case 0x0703: //RxEnterBase(player, msg); { var pos = 2; var baseid = FLMsgType.GetUInt32(msg, ref pos); var acc = Context.Parent.Ask <PlayerActor.AccountShipData>(new PlayerActor.EnterBase(baseid), TimeSpan.FromMilliseconds(850)); _baseRef.Tell(new EnterBaseData(acc.Result.Account, acc.Result.ShipData, baseid), Context.Sender); } //_baseID = baseid; break; case 0x0803: //RxRequestBaseInfo(msg); _baseRef.Tell(new BaseInfoRequest(msg), Context.Sender); break; case 0x0903: //RxRequestLocationInfo(player, msg); _baseRef.Tell(new LocInfoRequest(msg), Context.Sender); break; case 0x0B03: //RxSystemSwitchOutComplete(player, msg); break; case 0x0C03: //RxObjectCollision(player, msg); break; case 0x0D03: //RxExitBase(player, msg); break; case 0x0E03: //RxEnterLocation(msg); { int pos = 2; uint roomid = FLMsgType.GetUInt32(msg, ref pos); _baseRef.Tell(new EnterLocation(roomid), Context.Sender); } break; case 0x0F03: //RxExitLocation(player, msg); { int pos = 2; uint roomid = FLMsgType.GetUInt32(msg, ref pos); _baseRef.Tell(new ExitLocation(roomid), Context.Sender); } break; case 0x1003: //RxRequestCreateShip(player, msg); break; case 0x1103: //RxGoodSell(player, msg); - we ignore this, look RxRequestRemoveItem break; case 0x1203: //RxGoodBuy(player, msg); - we ignore this, look RxRequestAddItem break; case 0x1303: //RxGFSelectObject(player, msg); { var pos = 2; var charid = FLMsgType.GetUInt32(msg, ref pos); _baseRef.Tell(new SelectObject(charid)); } break; case 0x1403: //RxMissionResponse(player, msg); break; case 0x1503: //RxRequestShipArch(player, msg); break; case 0x1603: //RxRequestEquipment(player, msg); break; case 0x1803: //RxRequestAddItem(player, msg); _baseRef.Tell(new RequestAddItem(msg), Context.Sender); break; case 0x1903: //RxRequestRemoveItem(player, msg); break; case 0x1B03: //RxRequestSetCash(player, msg); - we ignore this, count based on buy\sell, RxRequest{add,remove}Item break; case 0x1C03: //RxRequestChangeCash(player, msg); - we ignore this, count based on buy\sell, RxRequest{add,remove}Item break; case 0x2F03: //RxSetManeuver(player, msg); break; case 0x3103: //RxRequestEvent(player, msg); break; case 0x3203: //RxRequestCancel(player, msg); break; case 0x3b03: //RxRequestSetHullStatus(player, msg); break; case 0x3E03: //RxLaunchComplete(player, msg); break; case 0x3F03: //RxClientHail(player, msg); break; case 0x4003: //RxRequestUseItem(player, msg); break; case 0x4303: //RxJumpInComplete(player, msg); break; case 0x4403: //RxRequestInvincibility(player, msg); break; default: // Unexpected packet. Log and ignore it. _log.Warn("Unexpected message from FLID {0} in InBaseState: {1}", _flPlayerID, BitConverter.ToString(msg)); break; } } else { // Unexpected packet. Log and ignore it. _log.Warn("Unexpected message from FLID {0} in InBaseState: {1}", _flPlayerID, BitConverter.ToString(msg)); } }
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 (_dplaySessions) { _dplaySessions[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); } } } }
/// <summary> /// FLPACKET_SERVER_LAUNCH /// </summary> /// <param name="player"></param> public static void SendServerLaunch(Player player) { { Vector eulerRot = Matrix.MatrixToEulerDeg(player.Ship.Orientation); player.Log.AddLog(LogType.FL_MSG, "tx FLPACKET_SERVER_SERVERLAUNCH objid={0} position={1} orient={2}", player.Ship.Objid, player.Ship.Position, eulerRot); // If we're spawning in a base solar, send the solar id if (player.Ship.CurrentAction is LaunchFromBaseAction) { var action = player.Ship.CurrentAction as LaunchFromBaseAction; player.Ship.Position = action.Position; player.Ship.Orientation = Quaternion.QuaternionToMatrix(action.Orientation); byte[] omsg = { 0x07, 0x02 }; FLMsgType.AddUInt32(ref omsg, player.Ship.Objid); FLMsgType.AddUInt32(ref omsg, action.DockingObj.Solar.Objid); FLMsgType.AddUInt32(ref omsg, action.DockingObj.Index); FLMsgType.AddFloat(ref omsg, (float)player.Ship.Position.x); FLMsgType.AddFloat(ref omsg, (float)player.Ship.Position.y); FLMsgType.AddFloat(ref omsg, (float)player.Ship.Position.z); Quaternion q = Quaternion.MatrixToQuaternion(player.Ship.Orientation); FLMsgType.AddFloat(ref omsg, (float)q.W); FLMsgType.AddFloat(ref omsg, (float)q.I); FLMsgType.AddFloat(ref omsg, (float)q.J); FLMsgType.AddFloat(ref omsg, (float)q.K); player.SendMsgToClient(omsg); action.DockingObj.Activate(player.Runner, player.Ship); } // Otherwise we're spawning in a space or at a jump/moor point else if (player.Ship.CurrentAction is LaunchInSpaceAction) { var action = player.Ship.CurrentAction as LaunchInSpaceAction; player.Ship.Position = action.Position; player.Ship.Orientation = Quaternion.QuaternionToMatrix(action.Orientation); byte[] omsg = { 0x07, 0x02 }; FLMsgType.AddUInt32(ref omsg, player.Ship.Objid); FLMsgType.AddUInt32(ref omsg, 0); FLMsgType.AddInt32(ref omsg, -1); FLMsgType.AddFloat(ref omsg, (float)player.Ship.Position.x); FLMsgType.AddFloat(ref omsg, (float)player.Ship.Position.y); FLMsgType.AddFloat(ref omsg, (float)player.Ship.Position.z); FLMsgType.AddFloat(ref omsg, (float)action.Orientation.W); FLMsgType.AddFloat(ref omsg, (float)action.Orientation.I); FLMsgType.AddFloat(ref omsg, (float)action.Orientation.J); FLMsgType.AddFloat(ref omsg, (float)action.Orientation.K); player.SendMsgToClient(omsg); } } { byte[] omsg = { 0x54, 0x02, 0x09, 0x00 }; // flag (objid + dunno) FLMsgType.AddUInt32(ref omsg, player.Ship.Objid); FLMsgType.AddUInt32(ref omsg, 0); // dunnno player.SendMsgToClient(omsg); } { byte[] omsg = { 0x54, 0x02, 0x28, 0x00 }; // flag (faction + objid) FLMsgType.AddUInt32(ref omsg, player.Ship.Objid); FLMsgType.AddFloat(ref omsg, 0); // faction player.SendMsgToClient(omsg); } player.Ship.Basedata = null; }
/// <summary> /// Rewrite of <see cref="FLServer.Server.DirectPlayServer.ProcessPktFromClient"/> /// </summary> /// <param name="message"></param> public void Handle(RxMessage message) { var pos = 0; uint cmd = FLMsgType.GetUInt8(message.Bytes, ref pos); if (cmd == 0x00 && message.Bytes.Length >= 4) { uint opcode = FLMsgType.GetUInt8(message.Bytes, ref pos); if (opcode != 0x02 || message.Bytes.Length < 4) { return; } //That's enum request, spit out server info. uint enumPayload = FLMsgType.GetUInt16(message.Bytes, ref pos); var glob = Context.ActorSelection("/user/server/globals"); glob.Tell(new EnumRequest((ushort)enumPayload)); } // 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) && message.Bytes.Length >= 12) { uint opcode = FLMsgType.GetUInt8(message.Bytes, ref pos); // The CONNECT packet is used to request a connection. If accepted, the response // is a CONNECTED (section 2.2.1.2) packet switch (opcode) { case 0x01: { //now created on session start //var session = Context.Child("dplay-session"); //if (session.IsNobody()) //{ //session = Context.ActorOf<DPlaySession.DPlaySession>("dplay-session"); //Context.ActorOf<Congestion.Congestion>("congestion"); //} if (!_dpInitialized) { _dpInitialized = true; } var msgID = FLMsgType.GetUInt8(message.Bytes, ref pos); var rspID = FLMsgType.GetUInt8(message.Bytes, ref pos); var version = FLMsgType.GetUInt32(message.Bytes, ref pos); var dplayid = FLMsgType.GetUInt32(message.Bytes, ref pos); var timestamp = FLMsgType.GetUInt32(message.Bytes, ref pos); // 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 the session is fully connected because the client has // sent us a connect acknowledge then ignore this. //if (sess.SessionState == Session.State.CONNECTED) // return; //Whole thing done in DPlaySession //this part is done in ConnectingMessage handler //we're nullifying all counters _dplaySession.Tell(new DPlaySession.DPlaySession.ConnectRequest(msgID, dplayid, rspID), Context.Child("socket")); } break; case 0x06: { var flags = FLMsgType.GetUInt8(message.Bytes, ref pos); var retry = FLMsgType.GetUInt8(message.Bytes, ref pos); // The seq field indicates the seq of the next message that the client will send. var seq = FLMsgType.GetUInt8(message.Bytes, ref pos); // The next_rx field indicates the message seq that the client is waiting to receive var nrcv = FLMsgType.GetUInt8(message.Bytes, ref pos); pos += 2; // skip padding var timestamp = FLMsgType.GetUInt32(message.Bytes, ref pos); // Ignore packets for sessions that don't exist if (!_dpInitialized) { return; } //sess.BytesRx += pkt.Length; // If the hi sack mask is present, resend any requested packets. if ((flags & 0x02) == 0x02) { var mask = FLMsgType.GetUInt32(message.Bytes, ref pos); _congestion.Tell(new Congestion.Congestion.DoRetryOnSACKMessage(mask, nrcv)); } // If the hi sack mask is present, resend any requested packets. if ((flags & 0x04) == 0x04) { var mask = FLMsgType.GetUInt32(message.Bytes, ref pos); _congestion.Tell(new Congestion.Congestion.DoRetryOnSACKMessage(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. _congestion.Tell(new Congestion.Congestion.AckUserData(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 (cmd == 0x88) { _congestion.Tell("SendSACK"); } } break; } } // 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 && message.Bytes.Length >= 4) { uint control = FLMsgType.GetUInt8(message.Bytes, ref pos); var seq = FLMsgType.GetUInt8(message.Bytes, ref pos); var nrcv = FLMsgType.GetUInt8(message.Bytes, ref pos); // Ignore packets for sessions that don't exist //Session sess = GetSession(client); //if (sess == null) //return; // 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) { Context.ActorSelection("./*").Tell(PoisonPill.Instance, Context.Self); _log.Debug("{0} shutting down: Client request", Self.Path); SendCmdHardDisconnect(); Context.Stop(Context.Self); return; } //var cong = Context.Child("congestion"); //if (cong.IsNobody()) return; if (!_dpInitialized) { return; } var ticks = new TimeSpan(45); // 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. { var resp = _congestion.Ask <bool>(new Congestion.Congestion.AckIfInWindow(seq)); resp.Wait(ticks); if (resp.Result) { 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. { //var ask = new Congestion.CheckIfOutOfOrder(seq, message.Bytes); var resp = _congestion.Ask <bool>(new Congestion.Congestion.CheckIfOutOfOrder(seq, message.Bytes)); resp.Wait(ticks); if (resp.Result) { _log.Debug("{0} enqueued out of order packet {1}", Context.Self.Path, seq); 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) { _congestion.Tell("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) { var mask = FLMsgType.GetUInt32(message.Bytes, ref pos); _congestion.Tell(new Congestion.Congestion.DoRetryOnSACKMessage(mask, nrcv)); } if ((control & 0x20) == 0x20) { var mask = FLMsgType.GetUInt32(message.Bytes, ref pos); _congestion.Tell(new Congestion.Congestion.DoRetryOnSACKMessage(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(message.Bytes, ref pos); } if ((control & 0x80) == 0x80) { FLMsgType.GetUInt32(message.Bytes, 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 later // and process this message. _dplaySession.Tell(new DPlaySession.DPlaySession.ByteMessage(message.Bytes, pos), Context.Child("socket")); //ProcessTransUserData(sess, message.Bytes, pos); // If there are queued out of order packets, try to process these. // Done in Congestion on NextRxSeq // Advance the sequence number as well. _congestion.Tell("NextRxSeq"); // 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. _congestion.Tell(new Congestion.Congestion.AckUserData(nrcv)); // We always do an immediate acknowledge as bandwidth isn't a particular concern // but fast recovery from lost packets is. _congestion.Tell("SendSACK"); } }
public static void SendMiscObjUpdate(Player player, Player.MiscObjUpdateType update, params uint[] values) { switch (update) { case Player.MiscObjUpdateType.RANK: { player.Log.AddLog(LogType.FL_MSG, "tx FLPACKET_SERVER_MISCOBJUPDATE type=rank playerid={0} rank={1}", values[0], values[1]); byte[] omsg = { 0x54, 0x02, 0x44, 0x00 }; FLMsgType.AddUInt32(ref omsg, values[0]); FLMsgType.AddUInt16(ref omsg, values[1]); player.SendMsgToClient(omsg); return; } case Player.MiscObjUpdateType.SYSTEM: { player.Log.AddLog(LogType.FL_MSG, "tx FLPACKET_SERVER_MISCOBJUPDATE type=system playerid={0} systemid={1}", values[0], values[1]); byte[] omsg = { 0x54, 0x02, 0x84, 0x00 }; FLMsgType.AddUInt32(ref omsg, values[0]); FLMsgType.AddUInt32(ref omsg, values[1]); player.SendMsgToClient(omsg); return; } case Player.MiscObjUpdateType.GROUP: { player.Log.AddLog(LogType.FL_MSG, "tx FLPACKET_SERVER_MISCOBJUPDATE type=group playerid={0} group={1}", values[0], values[1]); byte[] omsg = { 0x54, 0x02, 0x05, 0x00 }; FLMsgType.AddUInt32(ref omsg, values[0]); FLMsgType.AddUInt32(ref omsg, values[1]); player.SendMsgToClient(omsg); return; } case Player.MiscObjUpdateType.UNK2: { player.Log.AddLog(LogType.FL_MSG, "tx FLPACKET_SERVER_MISCOBJUPDATE type=unknown2 objid={0}", values[0]); byte[] omsg = { 0x54, 0x02, 0x28, 0x00 }; FLMsgType.AddUInt32(ref omsg, values[0]); FLMsgType.AddInt32(ref omsg, -1); // faction? player.SendMsgToClient(omsg); return; } case Player.MiscObjUpdateType.UNK3: { player.Log.AddLog(LogType.FL_MSG, "tx FLPACKET_SERVER_MISCOBJUPDATE type=unknown3 objid={0}", values[0]); byte[] omsg = { 0x54, 0x02, 0x09, 0x00 }; FLMsgType.AddUInt32(ref omsg, values[0]); FLMsgType.AddUInt32(ref omsg, 0); player.SendMsgToClient(omsg); return; } default: return; } }
/// <summary> /// Process a charinforequest from a player. /// </summary> /// <param name="player"></param> public static void SendCharInfoRequestResponse(Player player) { List <Account> accs = Database.GetAccount(player.AccountID); //string accdir_path = Runner.Server.AcctPath + Path.DirectorySeparatorChar + //FLMsgType.FLNameToFile(AccountID); try { byte[] omsg = { 0x03, 0x02 }; FLMsgType.AddUInt8(ref omsg, 0); // chars //foreach (var path in Directory.GetFiles(accdir_path, "??-????????.fl")) if (accs != null) { foreach (Account acct in accs) { try { var dummy = new CharacterData { Ship = new Old.Object.Ship.Ship(null) }; // If the charfile is not valid ignore it. string result = dummy.LoadCharFile(acct, player.Log); if (result != null) { player.Log.AddLog(LogType.ERROR, "error: " + result); continue; } FLMsgType.AddAsciiStringLen16(ref omsg, FLMsgType.FLNameToFile(acct.CharName)); FLMsgType.AddUInt16(ref omsg, 0); FLMsgType.AddUnicodeStringLen16(ref omsg, acct.CharName); FLMsgType.AddUnicodeStringLen16(ref omsg, ""); FLMsgType.AddUInt32(ref omsg, 0); FLMsgType.AddUInt32(ref omsg, 0); FLMsgType.AddUInt32(ref omsg, 0); FLMsgType.AddUInt32(ref omsg, acct.Ship); if (dummy.Money > 2000000000) { FLMsgType.AddInt32(ref omsg, 2000000000); } else { FLMsgType.AddInt32(ref omsg, acct.Money); } FLMsgType.AddUInt32(ref omsg, dummy.Ship.System.SystemID); if (dummy.Ship.Basedata != null) { FLMsgType.AddUInt32(ref omsg, dummy.Ship.Basedata.BaseID); } else { FLMsgType.AddUInt32(ref omsg, 0); } FLMsgType.AddUInt32(ref omsg, 0); FLMsgType.AddUInt32(ref omsg, dummy.Ship.voiceid); FLMsgType.AddUInt32(ref omsg, dummy.Ship.Rank); FLMsgType.AddUInt32(ref omsg, 0); FLMsgType.AddFloat(ref omsg, dummy.Ship.Health); FLMsgType.AddUInt32(ref omsg, 0); FLMsgType.AddUInt32(ref omsg, 0); FLMsgType.AddUInt32(ref omsg, 0); FLMsgType.AddUInt8(ref omsg, 1); FLMsgType.AddUInt32(ref omsg, dummy.Ship.com_body); FLMsgType.AddUInt32(ref omsg, dummy.Ship.com_head); FLMsgType.AddUInt32(ref omsg, dummy.Ship.com_lefthand); FLMsgType.AddUInt32(ref omsg, dummy.Ship.com_righthand); FLMsgType.AddUInt32(ref omsg, 0); FLMsgType.AddUInt8(ref omsg, 1); FLMsgType.AddUInt32(ref omsg, dummy.Ship.com_body); FLMsgType.AddUInt32(ref omsg, dummy.Ship.com_head); FLMsgType.AddUInt32(ref omsg, dummy.Ship.com_lefthand); FLMsgType.AddUInt32(ref omsg, dummy.Ship.com_righthand); FLMsgType.AddUInt32(ref omsg, 0); FLMsgType.AddUInt8(ref omsg, (uint)dummy.Ship.Items.Count); foreach (ShipItem item in dummy.Ship.Items.Values) { FLMsgType.AddUInt32(ref omsg, item.count); FLMsgType.AddFloat(ref omsg, item.health); FLMsgType.AddUInt32(ref omsg, item.arch.ArchetypeID); FLMsgType.AddUInt16(ref omsg, item.hpid); FLMsgType.AddUInt16(ref omsg, (item.mounted ? 1u : 0u)); if (item.hpname.Length > 0) { FLMsgType.AddAsciiStringLen16(ref omsg, item.hpname + "\0"); } else { FLMsgType.AddAsciiStringLen16(ref omsg, "BAY\0"); } } FLMsgType.AddUInt32(ref omsg, 0); omsg[2]++; } catch (Exception e) { player.Log.AddLog(LogType.ERROR, "error: corrupt file when processing charinforequest '{0}'", e.Message); } } } FLMsgType.AddUInt32(ref omsg, 0); player.SendMsgToClient(omsg); } catch (Exception e) { player.Log.AddLog(LogType.ERROR, "error: unable to process charinforequest '{0}'", e.Message); } }
public void AddStyle(uint style) { FLMsgType.AddUInt32(ref _msg, 0x06); // rdl type of style FLMsgType.AddUInt32(ref _msg, 0x02); // size of data FLMsgType.AddUInt16(ref _msg, style); }
public void AddText(string text) { FLMsgType.AddUInt32(ref _msg, 0x02); FLMsgType.AddUInt32(ref _msg, 2 + (uint)text.Length * 2); FLMsgType.AddUnicodeStringLen0(ref _msg, text + "\0"); }
/// <summary> /// Send a dummy trans /// </summary> private void SendTUDSessionInfo() { 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 var resp = _globals.Ask <Globals.ServerInfo>("GetInfo"); resp.Wait(5); var result = resp.Result; FLMsgType.AddUInt32(ref pkt, result.MaxPlayers + 1); // dwMaxPlayers FLMsgType.AddUInt32(ref pkt, (uint)result.CurrentPlayers + 1); // dwCurrentPlayers FLMsgType.AddUInt32(ref pkt, 0x6C + 0x60); // dwSessionNameOffset FLMsgType.AddUInt32(ref pkt, (uint)result.ServerName.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, result.InstanceGUID); FLMsgType.AddArray(ref pkt, result.AppGUID); FLMsgType.AddUInt32(ref pkt, _dPlayID); // dpnid FLMsgType.AddUInt32(ref pkt, _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, _dPlayID); // dpnid FLMsgType.AddUInt32(ref pkt, 0); // dpnidOwner FLMsgType.AddUInt32(ref pkt, 0x020000); // dwFlags FLMsgType.AddUInt32(ref pkt, _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, result.ServerName); Context.ActorSelection("../congestion").Tell(pkt); //UserData.AddLast(pkt); //SendDFrame(sess); }