/// <summary> /// <para>ID 0x0436</para> /// <para>Length 19</para> /// <para>First packet after selecting a character</para> /// </summary> public static void WantToConnect(NetState state, PacketReader reader) { int accountID = reader.ReadInt32(); int charID = reader.ReadInt32(); int loginID1 = reader.ReadInt32(); int clientTick = reader.ReadInt32(); byte iSex = reader.ReadByte(); EAccountSex sex = (EAccountSex)iSex; state.Account = World.GetAccount(accountID); if (state.Account == null || state.Account.AccountState != EAccountState.Char) { state.Disconnect(); return; } if (state.Account.LoginID1 != loginID1 || state.Account.Sex != sex) { state.Disconnect(); return; } if (state.Account.ActiveChar == null || state.Account.ActiveChar.ID != charID) { state.Disconnect(); return; } // Mark as authed state.Account.AccountState = EAccountState.World; state.Send(new Response.WorldAuthOK(state.Account)); }
public static void AccountAuth(NetState state, PacketReader reader) { int accountID = reader.ReadInt32(); int loginID1 = reader.ReadInt32(); int loginID2 = reader.ReadInt32(); int unknown = reader.ReadInt16(); // offset 14 - 16 int iSex = reader.ReadByte(); EAccountSex sex = (EAccountSex)iSex; state.Account = (Account)World.Objects[EDatabaseType.Account, accountID]; state.Account.Netstate = state; if ( state.Account == null || state.Account.AccountState != EAccountState.Login || state.Account.LoginID1 != loginID1 || state.Account.LoginID2 != loginID2 || state.Account.Sex != sex ) { // Wrong data - hack attempt? state.Account = null; state.Send(new CharacterResponseError((byte)0)); return; } // Mark as authed in character server state.Account.AccountState = EAccountState.Char; state.Account.LoadChars(); // Auth successfull, send a special packet containing the AccountID state.Send(new CharacterResponseSuccess(state.Account)); // Send character list state.Send(new CharacterResponseList(state.Account)); }
/// <summary> /// <para>ID 0x007D</para> /// <para>Length 0</para> /// <para>Notification that the client ressource loading has completed</para> /// <para> /// No data given, but its the right place to load and send /// everything else. /// </para> /// </summary> public static void LoadEndAck(NetState state, PacketReader reader) { if (state.IsValid(EAccountState.World) == false) { state.Disconnect(); return; } // TODO: // - check if variable loading and every other hist has been finished // - check for rewarp (find out) // - changelook calls // - send inventory list // - check items // - if cart on, send cart-info and check items // - send weights // - if guild, send short guild info // - check for invicible timer // - clif_spawn() (.. what? Oo) // - if party, send infos // - check for pvp, gvg, duel, ... // - getareacharinfo for nearby objects // - pet, homunculus, mercanery // - if first connection, send complete status // - send friendlist info (friend logged in) // - login npc events // - if not first connect, refresh some infos // - many many more.. }
public static void CharacterCreation(NetState state, PacketReader reader) { if (state.Account == null || state.Account.AccountState != EAccountState.Char) { state.Disconnect(); return; } string name = reader.ReadString(24); byte attrStr = reader.ReadByte(); byte attrAgi = reader.ReadByte(); byte attrVit = reader.ReadByte(); byte attrInt = reader.ReadByte(); byte attrDex = reader.ReadByte(); byte attrLuk = reader.ReadByte(); byte slot = reader.ReadByte(); short hairColor = reader.ReadInt16(); short hairStyle = reader.ReadInt16(); Character newChar = null; ECharacterCreationResult result = Character.Create(state.Account, name, slot, attrStr, attrAgi, attrVit, attrInt, attrDex, attrLuk, hairColor, hairStyle, out newChar); if (result != ECharacterCreationResult.Success) { state.Send(new CharacterResponseCreation(result)); return; } // Creation was successfull, send new characterlist state.Send(new CharacterResponseNewData(newChar)); }
public static void KeepAlive(NetState state, PacketReader reader) { uint accountID = reader.ReadUInt32(); if (state.Account == null || state.Account.AccountState != EAccountState.Char || accountID != state.Account.WorldID.ID) { state.Disconnect(); return; } //state.UpdateAcitivty(); }
/// Notification of the state of client command /effect (CZ_LESSEFFECT) /// 021D 6: <state>.L public static void LessEffect(NetState state, PacketReader reader) { if (state.IsValid(EAccountState.World) == false) { state.Disconnect(); return; } // 0 = Full effects // 1 = Reduced effects int lessEffect = reader.ReadInt32(); }
public static bool ValidateMessage(NetState state, string text, out string username, out string message) { // Delimiter for <username> and <message> // and yep, this means, the client sends us "Username : message" as one string // so we have to parse it.. string delim = " : "; username = ""; message = ""; // TODO: i saw a topic in eA source request about fakename talking // he wanted to display the fakename in the chat // if implementing such a mod, we should check here for the fakename too! if (text.StartsWith(state.ActiveChar.Status.Name + delim) == false) { // This is just to check for a hacked packet // The message always has to start with <username><delimiter>, so its incorrect either @ this point if (text.StartsWith(state.ActiveChar.Status.Name) == false) { ServerConsole.DebugLine("Chatting.ValidateMessage: Player '{0}' sent a message using an incorrect name! Forcing a relog...", state.ActiveChar.Status.Name); state.Dispose(); } return false; } // Split by delimiter string[] messageParts = text.Split(new string[] { delim }, StringSplitOptions.None); // Just in case.. if (messageParts.Length != 2) { return false; } // TODO: couldnt trim here to not modify the message, but i should.. maybe.. or not? o.o username = messageParts[0]; message = messageParts[1]; // Remove |00 if (message.StartsWith("|00") == true) { message = message.Substring(3); } // TODO: We didnt have max size for message as eAthena // but the client has (max display size is 254 + 1) // so.. ? o.o if (message.Length >= Global.CHAT_SIZE_MAX) { ServerConsole.WarningLine("Chatting.ValidateMessage: Message is to long! Max {0} length, msg has {1}: {2}", Global.CHAT_SIZE_MAX, message.Length, message); message = message.Substring(0, Global.CHAT_SIZE_MAX - 1); } return true; }
public static void CharacterSelect(NetState state, PacketReader reader) { short slot = reader.ReadByte(); if (state.Account == null || state.Account.AccountState != EAccountState.Char) { state.Send(new CharacterResponseError((byte)0)); state.Disconnect(); return; } if (state.Account.HasSlot(slot) == false) { state.Send(new CharacterResponseError((byte)0)); state.Disconnect(); return; } Character selectedChar = state.Account.SetActiveChar(slot); state.Send(new CharacterResponseWorldLogin(selectedChar)); }
public static void AccountLogin(NetState state, PacketReader reader) { int clientVersion = reader.ReadInt32(); string loginName = reader.ReadString(24); string loginPassword = reader.ReadString(24); byte clientType = reader.ReadByte(); Account acc; EAccountLoadResult result = Account.Load(state, loginName, loginPassword, out acc, false); if (result != EAccountLoadResult.Success) { state.Send(new Response.LoginResponseAccountError((byte)result)); state.Disconnect(); return; } acc.AccountState = EAccountState.Login; acc.UpdateLastLogin(state.Address.ToString()); state.Send(new Response.LoginResponseServerList(acc)); }
public virtual bool OnBeforeSend(NetState state) { // @TODO: delegates & events! // Return true to send return true; }
public static void SpawnOnce(NetState state, Location loc, string name, int mobID, int amount) { // TODO: calllback/event Character character = (state != null && state.Account != null && state.Account.ActiveChar != null ? state.Account.ActiveChar : null); int lv = (int)(character != null ? character.Status.LevelBase : 255); mobID = (mobID >= 0 ? mobID : GetRandomMobID(state.Account.ActiveChar, -mobID - 1, 3, lv)); amount = Math.Min(amount, 1000); for (int i = 0; i < amount; i++) { Monster mob = SpawnOnceSub(character, loc, name, mobID); if (mob == null) { continue; } // TODO: check for emperium, add guardian & castle data mob.Spawn(); // TODO: check for negative mobID (Dead branch) and mode changes } }
public static void WalkToXY(SerialObject obj, NetState state) { Timer.DelayCall(TimeSpan.FromMilliseconds(120), new TimerStateCallback<SerialObject>(WalkToXY_Tick), obj); }
public virtual void OnSend(NetState state, bool wasSend) { // Update internal state if ((mState & (EPacketState.Acquired | EPacketState.Static)) == 0) { Free(); } }
/// 014f 6: <state>.L public static void GuildRequestInfo(NetState state, PacketReader reader) { if (state.IsValid(EAccountState.World) == false) { state.Disconnect(); return; } Character c = state.Account.ActiveChar; // Nothing to send? if (!(c.GuildID is Serial)) { // TODO: or battleground ID return; } int requestType = reader.ReadInt32(); switch (requestType) { case 0: //clif_guild_basicinfo(sd); //clif_guild_allianceinfo(sd); break; case 1: //clif_guild_positionnamelist(sd); //clif_guild_memberlist(sd); break; case 2: //clif_guild_positionnamelist(sd); //clif_guild_positioninfolist(sd); break; case 3: //clif_guild_skillinfo(sd); break; case 4: //clif_guild_expulsionlist(sd); break; default: ServerConsole.ErrorLine("GuildRequestInfo: Unknown request type {0}", requestType); break; } }
/// <summary> /// Try to load a specific account from the database /// </summary> /// <param name="state"></param> /// <param name="Name"></param> /// <param name="Password"></param> /// <param name="Acc"></param> /// <returns>EAccountLoadResult</returns> public static EAccountLoadResult Load(NetState state, string Name, string Password, out Account Acc, bool loadChars) { // Lookup on object manager if ((Acc = World.Objects.SearchAccount(Name)) != null) { if (loadChars == true) { Acc.LoadChars(); } Acc.Netstate = state; return EAccountLoadResult.Success; } // Not found, load from Database Acc = new Account(true); // auto-push to world Acc.Netstate = state; DataTable table = Core.Database.Query("SELECT a.*, COUNT(*) AS charCount FROM account AS a LEFT JOIN `char` AS c ON c.accountID = a.accountID WHERE a.username = '******' LIMIT 0,1", Name.MysqlEscape()); table.TableName = "Account Table"; if (table.Rows.Count == 0) { return EAccountLoadResult.UnregisteredID; } DataRow accRow = table.Rows[0]; // Password check if (accRow.Field<string>("password") != Password) { Acc = null; return EAccountLoadResult.IncorrectPassword; } Rovolution.Server.Database.RovolutionDatabase.PrintTableTypes(accRow.Table); // Enfore a serial value equal to the account ID // Assumes our Account creation script wont take a same ID twice Acc.WorldID = new WorldID(accRow.Field<uint>("accountID"), EDatabaseType.Account); Acc.Name = accRow.Field<string>("username"); Acc.Password = accRow.Field<string>("password"); Acc.Sex = (accRow.Field<string>("sex").ToLower() == "m" ? EAccountSex.Male : EAccountSex.Female); Acc.Email = accRow.Field<string>("email"); Acc.GMLevel = accRow.FieldNull<sbyte>("gmLevel"); Acc.LastIP = accRow.Field<string>("lastIP"); Acc.LastLogin = accRow.FieldDateTime("lastLogin"); Acc.LoginID1 = mLoginRandomizer.Next() + 1; Acc.LoginID2 = mLoginRandomizer.Next() + 1; // `accountState`, `unbanTime`, `expirationTime`, `loginCount`, `birthDate` if (loadChars == true) { Acc.LoadChars(); } return EAccountLoadResult.Success; }
public static void Execute(NetState state, string name) { }
public bool HandleReceive(NetState ns) { if (ns == null || ns.Running == false) { return false; } ByteQueue buffer = ns.Buffer; if (buffer == null || buffer.Length <= 0) return true; ServerConsole.DebugLine("{0}: Incoming data, {1} bytes", ns, buffer.Length); /* * Packet Analyse/verify && Parsing */ lock (buffer) { int turns = 0; int length = buffer.Length; while (length > 0 && ns != null && ns.Running) { short packetID = buffer.GetPacketID(); #if DEBUG_PACKETS // debug log using (TextWriter writer = File.CreateText(AppDomain.CurrentDomain.BaseDirectory + @"\packet_" + DateTime.Now.UnixTimestamp() + ".log")) { using (MemoryStream ms = new MemoryStream(buffer.ByteBuffer)) Tools.FormatBuffer(writer, ms, (int)ms.Length); } #endif PacketHandler handler = PacketHandlers.GetHandler(packetID); if (handler == null) { byte[] data = new byte[length]; length = buffer.Dequeue(data, 0, length); // Log unknown packets string unknownPacketPath = AppDomain.CurrentDomain.BaseDirectory + @"\packet_" + packetID.ToString("X4") + ".log"; if (File.Exists(unknownPacketPath) == false) { using (TextWriter writer = File.CreateText(unknownPacketPath)) { writer.WriteLine("Unknown packet 0x" + packetID.ToString("X4") + ", length " + length); using (MemoryStream ms = new MemoryStream(data)) { Tools.FormatBuffer(writer, ms, (int)ms.Length); } } } ServerConsole.WarningLine("{0}: P {1:X4}, {2} bytes, no Handler found!", ns, packetID, length); break; } ServerConsole.StatusLine("{0}: [{1}] P {2:X4}, {3} bytes, handled {4}", ns, handler.Name, packetID, length, handler.Length); byte[] packetBuffer; // If we set the length to -1 (dynamic packet length), read the full buffer int bufferLength = (handler.Length > 0 ? handler.Length : length); if (bufferLength < mBufferSize) packetBuffer = mBuffers.AcquireBuffer(); else packetBuffer = new byte[bufferLength]; buffer.Dequeue(packetBuffer, 0, bufferLength); PacketReader r = new PacketReader(packetBuffer, bufferLength, 0, true); handler.OnReceive(ns, r); length -= bufferLength; if (bufferLength < mBufferSize) { mBuffers.ReleaseBuffer(packetBuffer); } else { packetBuffer = null; } turns++; } // end while()*/ } // end Lock() // Clear internal byte buffer for receive data if (ns != null && ns.Buffer != null) { ns.Buffer.Clear(); } return true; }
public virtual void OnSend(NetState state) { // Assume data was send OnSend(state, true); }
/// <summary> /// an Empty/Unused/not yet available Packet Handler /// </summary> /// <param name="state"></param> /// <param name="reader"></param> public static void EmptyHandler(NetState state, PacketReader reader) { }
/// Request servert ticks /// 0089 11: <clientTicks>.L public static void TickSend(NetState state, PacketReader reader) { if (state.IsValid(EAccountState.World) == false) { state.Disconnect(); return; } // TODO: has a length of 11.. // state.Account.ClientTick = reader.ReadInt32(); // TODO: send server ticks }
/// Validates and processes global messages /// 008C/00F3 -1: <packet len>.W <text>.?B (<name> : <message>) 00 public static void GlobalMessage(NetState state, PacketReader reader) { if (state.IsValid(EAccountState.World) == false) { state.Disconnect(); return; } short textLen = (short)(reader.ReadInt16() - 4); string text = reader.ReadString(textLen); // TODO: // - check name/message // - check atcommand // - process message to all in range (9 cells) // - trigger listening npc's // - log chat }
/// Requesting unit's name /// 0094 6: <object id>.L public static void CharNameRequest(NetState state, PacketReader reader) { if (state.IsValid(EAccountState.World) == false) { state.Disconnect(); return; } int id = reader.ReadInt32(); // Disguises using -ID.. if (id < 0 && -id == state.Account.ID) { id = (int)state.Account.ID; } SerialObject obj = World.GetObject(id); if (obj == null) { // Lagged clients could request names of already gone mobs/players.. // so no disconnect on invalid requests.. //state.Disconnect(); return; } if (obj.Location.Map != state.Account.ActiveChar.Location.Map) { // || !check_distance_bl(&sd->bl, bl, AREA_SIZE) return; // Block namerequests past view range } // 'see people in GM hide' cheat detection /* disabled due to false positives (network lag + request name of char that's about to hide = race condition) sc = status_get_sc(bl); if (sc && sc->option&OPTION_INVISIBLE && !disguised(bl) && bl->type != BL_NPC && //Skip hidden NPCs which can be seen using Maya Purple pc_isGM(sd) < battle_config.hack_info_GM_level ) { char gm_msg[256]; sprintf(gm_msg, "Hack on NameRequest: character '%s' (account: %d) requested the name of an invisible target (id: %d).\n", sd->status.name, sd->status.account_id, id); ShowWarning(gm_msg); // information is sent to all online GMs intif_wis_message_to_gm(wisp_server_name, battle_config.hack_info_GM_level, gm_msg); return; } */ // TODO: implement me :o //clif_charnameack(fd, bl); }
/// Request for game exiting /// 018A 4: <dont_know>.W public static void QuitGame(NetState state, PacketReader reader) { if (state.IsValid(EAccountState.World) == false) { state.Disconnect(); return; } // TODO: logout check state.Send(new Response.WorldConfirmGameExit(true)); }
public void OnReceive(NetState ns) { lock (this) mQueue.Enqueue(ns); Core.Set(); }
/// Request to walk /// 0x00A7 9: public static void WalkToXY(NetState state, PacketReader reader) { if (state.IsValid(EAccountState.World) == false) { state.Disconnect(); return; } Character c = state.Account.ActiveChar; int unkInt = reader.ReadInt32(); // Since 2008-08-27aRagexeRE, X/Y starts at pos 6 byte b1 = reader.ReadByte(); byte b2 = reader.ReadByte(); byte b3 = reader.ReadByte(); int x = b1 * 4 + (b2 >> 6); int y = ((b2 & 0x3f) << 4) + (b3 >> 4); Point2D targetLoc = new Point2D(x, y); c.TargetLocation = c.Location.Point + (c.Location.Point - targetLoc); }
/// <summary> /// Checks for new connections /// </summary> private void CheckListener() { Socket[] accepted = mListener.Slice(); for (int i = 0; i < accepted.Length; ++i) { NetState ns = new NetState(accepted[i], this); ns.Start(); if (ns.Running) { ServerConsole.StatusLine("{0}: Connected. [{1} Online]", ns, NetState.Instances.Count); } } }
/// <summary> /// Try to load a specific account from the database, including all characters /// </summary> /// <param name="State"></param> /// <param name="Name"></param> /// <param name="Password"></param> /// <param name="Acc"></param> /// <returns>EAccountLoadResult</returns> public static EAccountLoadResult Load(NetState State, string Name, string Password, out Account Acc) { return Load(State, Name, Password, out Acc, true); }