diff --git a/FFXIVClassic_Lobby_Server/ClientConnection.cs b/FFXIVClassic_Lobby_Server/ClientConnection.cs index 7670aea2..e326b3e1 100644 --- a/FFXIVClassic_Lobby_Server/ClientConnection.cs +++ b/FFXIVClassic_Lobby_Server/ClientConnection.cs @@ -26,6 +26,13 @@ namespace FFXIVClassic_Lobby_Server //Instance Stuff public uint currentUserId = 0; public uint currentAccount; + + //Chara Creation + public string newCharaName; + public uint newCharaPid; + public uint newCharaCid; + public ushort newCharaSlot; + public ushort newCharaWorldId; public void processIncoming(int bytesIn) diff --git a/FFXIVClassic_Lobby_Server/Database.cs b/FFXIVClassic_Lobby_Server/Database.cs index 7a3eaec2..bce3029a 100644 --- a/FFXIVClassic_Lobby_Server/Database.cs +++ b/FFXIVClassic_Lobby_Server/Database.cs @@ -1,5 +1,6 @@ using FFXIVClassic_Lobby_Server.dataobjects; using MySql.Data.MySqlClient; +using Dapper; using Newtonsoft.Json; using System; using System.Collections.Generic; @@ -21,7 +22,7 @@ namespace FFXIVClassic_Lobby_Server try { conn.Open(); - MySqlCommand cmd = new MySqlCommand("SELECT * FROM ffxiv_sessions WHERE id = @sessionId AND expiration > NOW()", conn); + MySqlCommand cmd = new MySqlCommand("SELECT * FROM sessions WHERE id = @sessionId AND expiration > NOW()", conn); cmd.Parameters.AddWithValue("@sessionId", sessionId); using (MySqlDataReader Reader = cmd.ExecuteReader()) { @@ -35,7 +36,7 @@ namespace FFXIVClassic_Lobby_Server { Console.WriteLine(e); } finally { - conn.Close(); + conn.Dispose(); } } return id; @@ -51,7 +52,7 @@ namespace FFXIVClassic_Lobby_Server conn.Open(); //Check if exists - MySqlCommand cmd = new MySqlCommand("SELECT * FROM ffxiv_characters2 WHERE name=@name AND serverId=@serverId", conn); + MySqlCommand cmd = new MySqlCommand("SELECT * FROM characters WHERE name=@name AND serverId=@serverId", conn); cmd.Parameters.AddWithValue("@serverId", serverId); cmd.Parameters.AddWithValue("@name", name); using (MySqlDataReader Reader = cmd.ExecuteReader()) @@ -67,16 +68,15 @@ namespace FFXIVClassic_Lobby_Server { MySqlCommand cmd2 = new MySqlCommand(); cmd2.Connection = conn; - cmd2.CommandText = "INSERT INTO ffxiv_characters2(userId, slot, serverId, name, state) VALUES(@userId, @slot, @serverId, @name, 0)"; + cmd2.CommandText = "INSERT INTO characters(userId, slot, serverId, name, state) VALUES(@userId, @slot, @serverId, @name, 0)"; cmd2.Prepare(); cmd2.Parameters.AddWithValue("@userId", userId); cmd2.Parameters.AddWithValue("@slot", slot); cmd2.Parameters.AddWithValue("@serverId", serverId); cmd2.Parameters.AddWithValue("@name", name); cmd2.ExecuteNonQuery(); - - pid = 1; - cid = 1; + cid = (ushort)cmd2.LastInsertedId; + pid = 0xBABE; } else { @@ -91,14 +91,14 @@ namespace FFXIVClassic_Lobby_Server } finally { - conn.Close(); + conn.Dispose(); } } return alreadyExists; } - public static void makeCharacter(uint accountId, String name, Character charaInfo) + public static void makeCharacter(uint accountId, uint cid, CharaInfo charaInfo) { using (MySqlConnection conn = new MySqlConnection(String.Format("Server={0}; Port={1}; Database={2}; UID={3}; Password={4}", ConfigConstants.DATABASE_HOST, ConfigConstants.DATABASE_PORT, ConfigConstants.DATABASE_NAME, ConfigConstants.DATABASE_USERNAME, ConfigConstants.DATABASE_PASSWORD))) { @@ -107,12 +107,13 @@ namespace FFXIVClassic_Lobby_Server conn.Open(); MySqlCommand cmd = new MySqlCommand(); cmd.Connection = conn; - cmd.CommandText = "UPDATE ffxiv_characters2 SET data=@encodedInfo WHERE accountId=@accountId AND name=@name"; + cmd.CommandText = "UPDATE characters SET state=3, charaInfo=@encodedInfo WHERE userId=@userId AND id=@cid"; cmd.Prepare(); - cmd.Parameters.AddWithValue("@accountId", accountId); - cmd.Parameters.AddWithValue("@name", name); - cmd.Parameters.AddWithValue("@encodedInfo", JsonConvert.SerializeObject(charaInfo)); + cmd.Parameters.AddWithValue("@userId", accountId); + cmd.Parameters.AddWithValue("@cid", cid); + string json = JsonConvert.SerializeObject(charaInfo); + cmd.Parameters.AddWithValue("@encodedInfo", json); cmd.ExecuteNonQuery(); } @@ -122,7 +123,7 @@ namespace FFXIVClassic_Lobby_Server } finally { - conn.Close(); + conn.Dispose(); } } } @@ -136,7 +137,7 @@ namespace FFXIVClassic_Lobby_Server conn.Open(); MySqlCommand cmd = new MySqlCommand(); cmd.Connection = conn; - cmd.CommandText = "UPDATE ffxiv_characters2 SET name=@name WHERE id=@cid"; + cmd.CommandText = "UPDATE characters SET name=@name WHERE id=@cid"; cmd.Prepare(); cmd.Parameters.AddWithValue("@cid", characterId); cmd.Parameters.AddWithValue("@name", newName); @@ -149,7 +150,7 @@ namespace FFXIVClassic_Lobby_Server } finally { - conn.Close(); + conn.Dispose(); } } } @@ -163,7 +164,7 @@ namespace FFXIVClassic_Lobby_Server conn.Open(); MySqlCommand cmd = new MySqlCommand(); cmd.Connection = conn; - cmd.CommandText = "UPDATE ffxiv_characters2 SET state=1 WHERE id=@cid AND name=@name"; + cmd.CommandText = "UPDATE characters SET state=1 WHERE id=@cid AND name=@name"; cmd.Prepare(); cmd.Parameters.AddWithValue("@cid", characterId); cmd.Parameters.AddWithValue("@name", name); @@ -176,61 +177,26 @@ namespace FFXIVClassic_Lobby_Server } finally { - conn.Close(); + conn.Dispose(); } } } public static List getServers() { - using (MySqlConnection conn = new MySqlConnection(String.Format("Server={0}; Port={1}; Database={2}; UID={3}; Password={4}", ConfigConstants.DATABASE_HOST, ConfigConstants.DATABASE_PORT, ConfigConstants.DATABASE_NAME, ConfigConstants.DATABASE_USERNAME, ConfigConstants.DATABASE_PASSWORD))) + using (var conn = new MySqlConnection(String.Format("Server={0}; Port={1}; Database={2}; UID={3}; Password={4}", ConfigConstants.DATABASE_HOST, ConfigConstants.DATABASE_PORT, ConfigConstants.DATABASE_NAME, ConfigConstants.DATABASE_USERNAME, ConfigConstants.DATABASE_PASSWORD))) { - List worldList = new List(); + List worldList = null; try { conn.Open(); - MySqlCommand cmd = new MySqlCommand(); - cmd.Connection = conn; - cmd.CommandText = "SELECT * FROM servers WHERE isActive=true"; - cmd.Prepare(); - MySqlDataReader Reader = cmd.ExecuteReader(); - - - if (!Reader.HasRows) return worldList; - while (Reader.Read()) - { - var id = Reader.GetUInt16("id"); - var name = Reader.GetString("name"); - var address = Reader.GetString("address"); - var port = Reader.GetUInt16("port"); - var listPosition = Reader.GetUInt16("listPosition"); - var numChars = Reader.GetUInt32("numChars"); - var maxChars = Reader.GetUInt32("maxChars"); - var isActive = Reader.GetBoolean("isActive"); - - if (isActive) - { - World world = new World(); - world.id = id; - world.name = name; - world.address = address; - world.port = port; - world.listPosition = listPosition; - uint result = (uint)(((float)numChars / (float)maxChars) * (float)100); - world.population = (ushort)result; - - world.isActive = isActive; - worldList.Add(world); - } - } - - + worldList = conn.Query("SELECT * FROM servers WHERE isActive=true").ToList(); } catch (MySqlException e) - { } + { worldList = new List(); } finally - { - conn.Close(); + { + conn.Dispose(); } return worldList; } @@ -238,46 +204,13 @@ namespace FFXIVClassic_Lobby_Server public static World getServer(uint serverId) { - using (MySqlConnection conn = new MySqlConnection(String.Format("Server={0}; Port={1}; Database={2}; UID={3}; Password={4}", ConfigConstants.DATABASE_HOST, ConfigConstants.DATABASE_PORT, ConfigConstants.DATABASE_NAME, ConfigConstants.DATABASE_USERNAME, ConfigConstants.DATABASE_PASSWORD))) + using (var conn = new MySqlConnection(String.Format("Server={0}; Port={1}; Database={2}; UID={3}; Password={4}", ConfigConstants.DATABASE_HOST, ConfigConstants.DATABASE_PORT, ConfigConstants.DATABASE_NAME, ConfigConstants.DATABASE_USERNAME, ConfigConstants.DATABASE_PASSWORD))) { World world = null; try { conn.Open(); - MySqlCommand cmd = new MySqlCommand(); - cmd.Connection = conn; - cmd.CommandText = "SELECT * FROM servers WHERE id='%serverId'"; - cmd.Prepare(); - cmd.Parameters.AddWithValue("@serverId", serverId); - - MySqlDataReader Reader = cmd.ExecuteReader(); - - if (!Reader.HasRows) return world; - while (Reader.Read()) - { - var id = Reader.GetUInt16("id"); - var name = Reader.GetString("name"); - var address = Reader.GetString("address"); - var port = Reader.GetUInt16("port"); - var listPosition = Reader.GetUInt16("listPosition"); - var numChars = Reader.GetUInt32("numChars"); - var maxChars = Reader.GetUInt32("maxChars"); - var isActive = Reader.GetBoolean("isActive"); - - if (isActive) - { - world = new World(); - world.id = id; - world.name = name; - world.address = address; - world.port = port; - world.listPosition = listPosition; - uint result = ((numChars / maxChars) * 0xFF) & 0xFF; - world.population = (ushort)result; - world.isActive = isActive; - } - } - + world = conn.Query("SELECT * FROM servers WHERE id=@ServerId", new {ServerId = serverId}).SingleOrDefault(); } catch (MySqlException e) { @@ -291,5 +224,25 @@ namespace FFXIVClassic_Lobby_Server } } + public static List getCharacters(uint userId) + { + using (var conn = new MySqlConnection(String.Format("Server={0}; Port={1}; Database={2}; UID={3}; Password={4}", ConfigConstants.DATABASE_HOST, ConfigConstants.DATABASE_PORT, ConfigConstants.DATABASE_NAME, ConfigConstants.DATABASE_USERNAME, ConfigConstants.DATABASE_PASSWORD))) + { + List charaList = null; + try + { + conn.Open(); + charaList = conn.Query("SELECT * FROM characters WHERE userId=@UserId AND state in (2,3) ORDER BY slot", new { UserId = userId }).ToList(); + } + catch (MySqlException e) + { charaList = new List(); } + finally + { + conn.Dispose(); + } + return charaList; + } + } + } } diff --git a/FFXIVClassic_Lobby_Server/FFXIVClassic_Lobby_Server.csproj b/FFXIVClassic_Lobby_Server/FFXIVClassic_Lobby_Server.csproj index 04d44339..4a963dbd 100644 --- a/FFXIVClassic_Lobby_Server/FFXIVClassic_Lobby_Server.csproj +++ b/FFXIVClassic_Lobby_Server/FFXIVClassic_Lobby_Server.csproj @@ -37,6 +37,9 @@ ..\packages\Cyotek.CircularBuffer.1.0.0.0\lib\net20\Cyotek.Collections.Generic.CircularBuffer.dll + + ..\packages\Dapper.1.42\lib\net45\Dapper.dll + ..\packages\MySql.Data.6.9.7\lib\net45\MySql.Data.dll @@ -53,6 +56,7 @@ + diff --git a/FFXIVClassic_Lobby_Server/PacketProcessor.cs b/FFXIVClassic_Lobby_Server/PacketProcessor.cs index 543d672c..46299c1a 100644 --- a/FFXIVClassic_Lobby_Server/PacketProcessor.cs +++ b/FFXIVClassic_Lobby_Server/PacketProcessor.cs @@ -154,13 +154,13 @@ namespace FFXIVClassic_Lobby_Server { Console.WriteLine("{0} => Get characters", client.currentUserId == 0 ? client.getAddress() : "User " + client.currentUserId); - sendWorldList(client, packet); + sendWorldList(client, packet); - BasePacket outgoingPacket = new BasePacket("./packets/getCharsPacket.bin"); + BasePacket outgoingPacket = new BasePacket("./packets/getChars_wo_chars"); BasePacket.encryptPacket(client.blowfish, outgoingPacket); client.queuePacket(outgoingPacket); - //sendCharacterList(client, packet); + sendCharacterList(client, packet); } private void ProcessSelectCharacter(ClientConnection client, SubPacket packet) @@ -207,9 +207,11 @@ namespace FFXIVClassic_Lobby_Server uint pid = 0, cid = 0; - World world = Database.getServer(worldId); - string worldName = null; + if (worldId == 0) + worldId = client.newCharaWorldId; + string worldName = null; + World world = Database.getServer(worldId); if (world != null) worldName = world.name; @@ -233,7 +235,7 @@ namespace FFXIVClassic_Lobby_Server if (alreadyTaken) { - ErrorPacket errorPacket = new ErrorPacket(charaReq.sequence, 0xBDB, 0, 13005, ""); + ErrorPacket errorPacket = new ErrorPacket(charaReq.sequence, 1003, 0, 13005, ""); //BDB - Chara Name Used, //1003 - Bad Word SubPacket subpacket = errorPacket.buildPacket(); BasePacket basePacket = BasePacket.createPacket(subpacket, true, false); BasePacket.encryptPacket(client.blowfish, basePacket); @@ -242,13 +244,25 @@ namespace FFXIVClassic_Lobby_Server Log.info(String.Format("User {0} => Error; name taken: \"{1}\"", client.currentUserId, charaReq.characterName)); return; } + else + { + pid = 0; + client.newCharaCid = cid; + client.newCharaSlot = slot; + client.newCharaWorldId = worldId; + client.newCharaName = name; + } Log.info(String.Format("User {0} => Character reserved \"{1}\"", client.currentUserId, charaReq.characterName)); break; case 0x02://Make - Character character = Character.EncodedToCharacter(charaReq.characterInfoEncoded); + CharaInfo info = new CharaInfo(); - Database.makeCharacter(client.currentUserId, name, character); + Database.makeCharacter(client.currentUserId, client.newCharaCid, info); + + pid = 1; + cid = client.newCharaCid; + name = client.newCharaName; Log.info(String.Format("User {0} => Character created \"{1}\"", client.currentUserId, charaReq.characterName)); break; @@ -299,13 +313,9 @@ namespace FFXIVClassic_Lobby_Server private void sendCharacterList(ClientConnection client, SubPacket packet) { - //List serverList = Database.getServers(); + List characterList = Database.getCharacters(client.currentUserId); - List charaList = new List(); - charaList.Add(new Character()); - charaList.Add(new Character()); - - CharacterListPacket characterlistPacket = new CharacterListPacket(1, charaList); + CharacterListPacket characterlistPacket = new CharacterListPacket(2, characterList); List subPackets = characterlistPacket.buildPackets(); subPackets[0].debugPrintSubPacket(); BasePacket basePacket = BasePacket.createPacket(subPackets, true, false); diff --git a/FFXIVClassic_Lobby_Server/app.config b/FFXIVClassic_Lobby_Server/app.config new file mode 100644 index 00000000..d0ac62a0 --- /dev/null +++ b/FFXIVClassic_Lobby_Server/app.config @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/FFXIVClassic_Lobby_Server/dataobjects/CharaInfo.cs b/FFXIVClassic_Lobby_Server/dataobjects/CharaInfo.cs new file mode 100644 index 00000000..3c96dc18 --- /dev/null +++ b/FFXIVClassic_Lobby_Server/dataobjects/CharaInfo.cs @@ -0,0 +1,57 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace FFXIVClassic_Lobby_Server.dataobjects +{ + class CharaInfo + { + public uint tribe = 0; + public uint size = 0; + public uint voice = 0; + public uint skinColor = 0; + + public uint hairStyle = 0; + public uint hairColor = 0; + public uint eyeColor = 0; + + public uint faceType = 0; + public uint faceBrow = 0; + public uint faceEye = 0; + public uint faceIris = 0; + public uint faceNose = 0; + public uint faceMouth = 0; + public uint faceJaw = 0; + public uint faceCheek = 0; + public uint faceOption1 = 0; + public uint faceOption2 = 0; + + public uint guardian = 0; + public uint birthMonth = 0; + public uint birthDay = 0; + public uint allegiance = 0; + + public uint weapon1 = 0; + public uint weapon2 = 0; + + public uint headGear = 0; + public uint bodyGear = 0; + public uint legsGear = 0; + public uint handsGear = 0; + public uint feetGear = 0; + public uint waistGear = 0; + public uint rightEarGear = 0; + public uint leftEarGear = 0; + public uint rightFingerGear = 0; + public uint leftFingerGear = 0; + + public byte[] toBytes() + { + byte[] bytes = new byte[0x120]; + return bytes; + } + + } +} diff --git a/FFXIVClassic_Lobby_Server/dataobjects/Character.cs b/FFXIVClassic_Lobby_Server/dataobjects/Character.cs index 5947f952..f9f2bc06 100644 --- a/FFXIVClassic_Lobby_Server/dataobjects/Character.cs +++ b/FFXIVClassic_Lobby_Server/dataobjects/Character.cs @@ -4,62 +4,23 @@ using System.Linq; using System.Text; using System.Threading.Tasks; using FFXIVClassic_Lobby_Server.common; +using FFXIVClassic_Lobby_Server.dataobjects; namespace FFXIVClassic_Lobby_Server { class Character { - public string name = "Test Test"; - public string world = "Test World"; - - public uint id = 0; - - public uint tribe = 0; - public uint size = 0; - public uint voice = 0; - public uint skinColor = 0; - - public uint hairStyle = 0; - public uint hairColor = 0; - public uint eyeColor = 0; - - public uint faceType = 0; - public uint faceBrow = 0; - public uint faceEye = 0; - public uint faceIris = 0; - public uint faceNose = 0; - public uint faceMouth = 0; - public uint faceJaw = 0; - public uint faceCheek = 0; - public uint faceOption1 = 0; - public uint faceOption2 = 0; - - public uint guardian = 0; - public uint birthMonth = 0; - public uint birthDay = 0; - public uint allegiance = 0; - - public uint weapon1 = 0; - public uint weapon2 = 0; - - public uint headGear = 0; - public uint bodyGear = 0; - public uint legsGear = 0; - public uint handsGear = 0; - public uint feetGear = 0; - public uint waistGear = 0; - public uint rightEarGear = 0; - public uint leftEarGear = 0; - public uint rightFingerGear = 0; - public uint leftFingerGear = 0; + public uint id; + public ushort slot; + public ushort serverId; + public string name; + public ushort state; + public string charaInfo; + public bool isLegacy; + public bool doRename; + public uint currentZoneId; - public byte[] toBytes() - { - byte[] bytes = new byte[0x120]; - return bytes; - } - - public static String characterToEncoded(Character chara) + public static String characterToEncoded(CharaInfo chara) { String charaInfo = System.Convert.ToBase64String(chara.toBytes()); charaInfo.Replace("+", "-"); @@ -67,7 +28,7 @@ namespace FFXIVClassic_Lobby_Server return charaInfo; } - public static Character EncodedToCharacter(String charaInfo) + public static CharaInfo EncodedToCharacter(String charaInfo) { charaInfo.Replace("+", "-"); charaInfo.Replace("/", "_"); @@ -77,7 +38,8 @@ namespace FFXIVClassic_Lobby_Server Console.WriteLine(Utils.ByteArrayToHex(data)); Console.WriteLine("------------Base64 printout------------------"); - Character chara = new Character(); + CharaInfo chara = new CharaInfo(); + return chara; } } diff --git a/FFXIVClassic_Lobby_Server/packages.config b/FFXIVClassic_Lobby_Server/packages.config index 0f477714..35470b0a 100644 --- a/FFXIVClassic_Lobby_Server/packages.config +++ b/FFXIVClassic_Lobby_Server/packages.config @@ -1,6 +1,7 @@  + \ No newline at end of file diff --git a/FFXIVClassic_Lobby_Server/packets/CharaCreatorPacket.cs b/FFXIVClassic_Lobby_Server/packets/CharaCreatorPacket.cs index ae46b83b..d78f1eaf 100644 --- a/FFXIVClassic_Lobby_Server/packets/CharaCreatorPacket.cs +++ b/FFXIVClassic_Lobby_Server/packets/CharaCreatorPacket.cs @@ -33,7 +33,7 @@ namespace FFXIVClassic_Lobby_Server.packets this.command = command; this.pid = pid; this.cid = cid; - this.type = 0x400000; + this.type = 0x400017; this.ticket = ticket; this.charaName = charaName; this.worldName = worldName; diff --git a/FFXIVClassic_Lobby_Server/packets/CharacterListPacket.cs b/FFXIVClassic_Lobby_Server/packets/CharacterListPacket.cs index 2cb0470a..208164e0 100644 --- a/FFXIVClassic_Lobby_Server/packets/CharacterListPacket.cs +++ b/FFXIVClassic_Lobby_Server/packets/CharacterListPacket.cs @@ -41,7 +41,7 @@ namespace FFXIVClassic_Lobby_Server.packets //Write List Info binWriter.Write((UInt64)sequence); - binWriter.Write(characterList.Count - totalCount <= MAXPERPACKET ? (byte)(characterList.Count + 1) : (byte)0); + binWriter.Write(characterList.Count - totalCount <= MAXPERPACKET ? (byte)(1) : (byte)0); //binWriter.Write((byte)1); binWriter.Write(characterList.Count - totalCount <= MAXPERPACKET ? (UInt32)(characterList.Count - totalCount) : (UInt32)MAXPERPACKET); binWriter.Write((byte)0); @@ -51,12 +51,26 @@ namespace FFXIVClassic_Lobby_Server.packets binWriter.Seek(0x10 + (0x1D0 * characterCount), SeekOrigin.Begin); //Write Entries + World world = Database.getServer(chara.serverId); + string worldname = world == null ? "Unknown" : world.name; + binWriter.Write((uint)0); //??? - binWriter.Write((uint)(totalCount + 1)); //Character Id - binWriter.Write((uint)totalCount); //Slot - binWriter.Write((uint)0); //Options (0x01: Service Account not active, 0x72: Change Chara Name) + binWriter.Write((uint)chara.id); //Character Id + binWriter.Write((byte)totalCount); //Slot + + byte options = 0; + if (chara.state == 2) + options |= 0x01; + if (chara.doRename) + options |= 0x02; + if (chara.isLegacy) + options |= 0x08; + + binWriter.Write((byte)options); //Options (0x01: Service Account not active, 0x72: Change Chara Name) + binWriter.Write((ushort)0); + binWriter.Write((uint)0xF4); //Logged out zone binWriter.Write(Encoding.ASCII.GetBytes(chara.name.PadRight(0x20, '\0'))); //Name - binWriter.Write(Encoding.ASCII.GetBytes(chara.world.PadRight(0xE, '\0'))); //World Name + binWriter.Write(Encoding.ASCII.GetBytes(worldname.PadRight(0xE, '\0'))); //World Name binWriter.Write("wAQAAOonIyMNAAAAV3Jlbml4IFdyb25nABwAAAAEAAAAAwAAAAMAAAA_8OADAAHQFAAEAAABAAAAABTQCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGEgAAAAMQAAQCQAAMAsAACKVAAAAPgCAAAAAAAAAAAAAAAAAAAAAAAAAAAAACQAAAAkAwAAAAAAAAAAANvb1M05AQAABBoAAAEABqoiIuIKAAAAcHJ2MElubjAxABEAAABkZWZhdWx0VGVycml0b3J5AAwJAhcABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAIAAAAAAAAAAAAAAAA="); //Appearance Data characterCount++;