diff --git a/FFXIVClassic Map Server/ClientConnection.cs b/FFXIVClassic Map Server/ClientConnection.cs index 5d2087a3..a5751a56 100644 --- a/FFXIVClassic Map Server/ClientConnection.cs +++ b/FFXIVClassic Map Server/ClientConnection.cs @@ -63,6 +63,11 @@ namespace FFXIVClassic_Lobby_Server return String.Format("{0}:{1}", (socket.RemoteEndPoint as IPEndPoint).Address, (socket.RemoteEndPoint as IPEndPoint).Port); } + public bool isConnected() + { + return (socket.Poll(1, SelectMode.SelectRead) && socket.Available == 0); + } + public void disconnect() { if (socket.Connected) diff --git a/FFXIVClassic Map Server/Database.cs b/FFXIVClassic Map Server/Database.cs index 6c4afeaf..8bc8e090 100644 --- a/FFXIVClassic Map Server/Database.cs +++ b/FFXIVClassic Map Server/Database.cs @@ -150,6 +150,132 @@ namespace FFXIVClassic_Lobby_Server } } + public static void savePlayerAppearance(Player player) + { + string query; + MySqlCommand cmd; + + 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))) + { + try + { + conn.Open(); + + query = @" + UPDATE characters_appearance SET + mainHand = @mainHand, + offHand = @offHand, + head = @head, + body = @body, + legs = @legs, + hands = @hands, + feet = @feet, + waist = @waist, + leftFinger = @leftFinger, + rightFinger = @rightFinger, + leftEar = @leftEar, + rightEar = @rightEar + WHERE characterId = @charaId + "; + + cmd = new MySqlCommand(query, conn); + cmd.Parameters.AddWithValue("@charaId", player.actorId); + cmd.Parameters.AddWithValue("@mainHand", player.appearanceIds[Character.MAINHAND]); + cmd.Parameters.AddWithValue("@offHand", player.appearanceIds[Character.OFFHAND]); + cmd.Parameters.AddWithValue("@head", player.appearanceIds[Character.HEADGEAR]); + cmd.Parameters.AddWithValue("@body", player.appearanceIds[Character.BODYGEAR]); + cmd.Parameters.AddWithValue("@legs", player.appearanceIds[Character.LEGSGEAR]); + cmd.Parameters.AddWithValue("@hands", player.appearanceIds[Character.HANDSGEAR]); + cmd.Parameters.AddWithValue("@feet", player.appearanceIds[Character.FEETGEAR]); + cmd.Parameters.AddWithValue("@waist", player.appearanceIds[Character.WAISTGEAR]); + cmd.Parameters.AddWithValue("@leftFinger", player.appearanceIds[Character.L_RINGFINGER]); + cmd.Parameters.AddWithValue("@rightFinger", player.appearanceIds[Character.R_RINGFINGER]); + cmd.Parameters.AddWithValue("@leftEar", player.appearanceIds[Character.L_EAR]); + cmd.Parameters.AddWithValue("@rightEar", player.appearanceIds[Character.R_EAR]); + + cmd.ExecuteNonQuery(); + } + catch (MySqlException e) + { Console.WriteLine(e); } + finally + { + conn.Dispose(); + } + } + } + + public static void savePlayerPosition(Player player) + { + string query; + MySqlCommand cmd; + + 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))) + { + try + { + conn.Open(); + + query = @" + UPDATE characters SET + positionX = @x, + positionY = @y, + positionZ = @z, + rotation = @rot, + currentZoneId = @zoneId + WHERE id = @charaId + "; + + cmd = new MySqlCommand(query, conn); + cmd.Parameters.AddWithValue("@charaId", player.actorId); + cmd.Parameters.AddWithValue("@x", player.positionX); + cmd.Parameters.AddWithValue("@y", player.positionY); + cmd.Parameters.AddWithValue("@z", player.positionZ); + cmd.Parameters.AddWithValue("@rot", player.rotation); + cmd.Parameters.AddWithValue("@zoneId", player.zoneId); + + cmd.ExecuteNonQuery(); + } + catch (MySqlException e) + { Console.WriteLine(e); } + finally + { + conn.Dispose(); + } + } + } + + public static void savePlayerPlayTime(Player player) + { + string query; + MySqlCommand cmd; + + 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))) + { + try + { + conn.Open(); + + query = @" + UPDATE characters SET + playTime = @playtime + WHERE id = @charaId + "; + + cmd = new MySqlCommand(query, conn); + cmd.Parameters.AddWithValue("@charaId", player.actorId); + cmd.Parameters.AddWithValue("@playtime", player.getPlayTime(true)); + + cmd.ExecuteNonQuery(); + } + catch (MySqlException e) + { Console.WriteLine(e); } + finally + { + conn.Dispose(); + } + } + } + public static void loadPlayerCharacter(Player player) { string query; @@ -164,7 +290,7 @@ namespace FFXIVClassic_Lobby_Server //Load basic info query = @" SELECT - name, + name, positionX, positionY, positionZ, @@ -184,7 +310,8 @@ namespace FFXIVClassic_Lobby_Server tribe, currentParty, restBonus, - achievementPoints + achievementPoints, + playTime FROM characters WHERE id = @charId"; cmd = new MySqlCommand(query, conn); @@ -214,10 +341,11 @@ namespace FFXIVClassic_Lobby_Server player.playerWork.tribe = reader.GetByte(17); player.playerWork.restBonusExpRate = reader.GetInt32(19); player.achievementPoints = reader.GetUInt32(20); + player.playTime = reader.GetUInt32(21); } } - player.charaWork.parameterSave.state_mainSkillLevel = 49; + player.charaWork.parameterSave.state_mainSkillLevel = 50; /* //Get level of our classjob @@ -312,8 +440,8 @@ namespace FFXIVClassic_Lobby_Server player.appearanceIds[Character.FACEINFO] = PrimitiveConversion.ToUInt32(CharacterUtils.getFaceInfo(reader.GetByte(8), reader.GetByte(9), reader.GetByte(10), reader.GetByte(11), reader.GetByte(12), reader.GetByte(13), reader.GetByte(14), reader.GetByte(15), reader.GetByte(16), reader.GetByte(17))); player.appearanceIds[Character.HIGHLIGHT_HAIR] = (uint)(reader.GetUInt16(6) | reader.GetUInt16(4) << 10); player.appearanceIds[Character.VOICE] = reader.GetByte(2); - player.appearanceIds[Character.WEAPON1] = reader.GetUInt32(18); - player.appearanceIds[Character.WEAPON2] = reader.GetUInt32(19); + player.appearanceIds[Character.MAINHAND] = reader.GetUInt32(18); + player.appearanceIds[Character.OFFHAND] = reader.GetUInt32(19); player.appearanceIds[Character.HEADGEAR] = reader.GetUInt32(20); player.appearanceIds[Character.BODYGEAR] = reader.GetUInt32(21); player.appearanceIds[Character.LEGSGEAR] = reader.GetUInt32(22); @@ -322,8 +450,8 @@ namespace FFXIVClassic_Lobby_Server player.appearanceIds[Character.WAISTGEAR] = reader.GetUInt32(25); player.appearanceIds[Character.R_EAR] = reader.GetUInt32(26); player.appearanceIds[Character.L_EAR] = reader.GetUInt32(27); - player.appearanceIds[Character.R_FINGER] = reader.GetUInt32(28); - player.appearanceIds[Character.L_FINGER] = reader.GetUInt32(29); + player.appearanceIds[Character.R_RINGFINGER] = reader.GetUInt32(28); + player.appearanceIds[Character.L_RINGFINGER] = reader.GetUInt32(29); } } @@ -947,5 +1075,6 @@ namespace FFXIVClassic_Lobby_Server return cheevosPacket.buildPacket(player.actorId); } + } } diff --git a/FFXIVClassic Map Server/PacketProcessor.cs b/FFXIVClassic Map Server/PacketProcessor.cs index 6a4e029e..37233416 100644 --- a/FFXIVClassic Map Server/PacketProcessor.cs +++ b/FFXIVClassic Map Server/PacketProcessor.cs @@ -146,8 +146,6 @@ namespace FFXIVClassic_Lobby_Server } else if (subpacket.header.type == 0x07) { - //Ping? - //packet.debugPrintPacket(); BasePacket init = Login0x7ResponsePacket.buildPacket(BitConverter.ToUInt32(packet.data, 0x10), Utils.UnixTimeStampUTC()); //client.queuePacket(init); } @@ -174,6 +172,7 @@ namespace FFXIVClassic_Lobby_Server //subpacket.debugPrintSubPacket(); PingPacket pingPacket = new PingPacket(subpacket.data); client.queuePacket(BasePacket.createPacket(PongPacket.buildPacket(player.actorID, pingPacket.time), true, false)); + player.ping(); break; //Unknown case 0x0002: @@ -254,7 +253,7 @@ namespace FFXIVClassic_Lobby_Server } } - mServer.GetLuaEngine().doActorOnEventStarted(player.getActor(), ownerActor, eventStart); + LuaEngine.doActorOnEventStarted(player.getActor(), ownerActor, eventStart); Log.debug(String.Format("\n===Event START===\nSource Actor: 0x{0:X}\nCaller Actor: 0x{1:X}\nVal1: 0x{2:X}\nVal2: 0x{3:X}\nEvent Starter: {4}\nParams: {5}", eventStart.actorID, eventStart.scriptOwnerActorID, eventStart.val1, eventStart.val2, eventStart.eventStarter, LuaUtils.dumpParams(eventStart.luaParams))); break; @@ -273,7 +272,7 @@ namespace FFXIVClassic_Lobby_Server break; } - mServer.GetLuaEngine().doActorOnEventUpdated(player.getActor(), updateOwnerActor, eventUpdate); + LuaEngine.doActorOnEventUpdated(player.getActor(), updateOwnerActor, eventUpdate); break; case 0x012F: diff --git a/FFXIVClassic Map Server/Program.cs b/FFXIVClassic Map Server/Program.cs index 5c9cc061..1ce41162 100644 --- a/FFXIVClassic Map Server/Program.cs +++ b/FFXIVClassic Map Server/Program.cs @@ -23,6 +23,14 @@ namespace FFXIVClassic_Lobby_Server #endif bool startServer = true; + Utils.FFXIVLoginStringDecodeBinary("C:\\Program Files (x86)\\SquareEnix\\FINAL FANTASY XIV\\ffxivlogin.exe"); + + Console.WriteLine(Utils.FFXIVLoginStringDecode(new byte[]{0x6F, 0xD6, 0x3C, 0xD6, 0x20, 0x81, 0x3F, 0x06, 0x36, 0x78, 0xD3, 0xAE, 0xDB, 0x4E, 0x08, 0xF1, 0x7D, 0xAE, 0x90, 0x43, 0x18, 0x70, 0x32, 0x08, 0x6B, 0x75, 0x98, 0xA1, 0x51, 0x15, 0xA9, 0xF7, 0x74, 0xB3, 0x6F, 0x10, 0xEA, 0x76, 0x34, 0x0B, 0x7E, 0x2D, 0xD2, 0xAC, 0xD7, 0xC3, 0xD3, 0xC1, 0x4D, 0x96, 0xED, 0xD4, 0xCC, 0x5E, 0x0D, 0xF5, 0x7E, 0x35, 0x99, 0xB9, 0x57, 0x38, 0x51, 0x79, 0x39, 0x3F, 0x08, 0xFB, 0xE8, 0xEE, 0x25, 0x4F, 0xAE, 0xE2, 0xFC, 0x7E, 0x2A, 0x72, 0x34, 0x57, 0x7E})); + + Console.WriteLine(Utils.ByteArrayToHex(Utils.FFXIVLoginStringEncode(0xd018, "http://141.117.163.26/login/"))); + + return; + Console.ForegroundColor = ConsoleColor.Cyan; Console.WriteLine("---------FFXIV 1.0 Map Server---------"); Console.ForegroundColor = ConsoleColor.Gray; diff --git a/FFXIVClassic Map Server/Server.cs b/FFXIVClassic Map Server/Server.cs index 67f69b0d..aaef19c6 100644 --- a/FFXIVClassic Map Server/Server.cs +++ b/FFXIVClassic Map Server/Server.cs @@ -25,6 +25,8 @@ namespace FFXIVClassic_Lobby_Server public const int FFXIV_MAP_PORT = 54992; public const int BUFFER_SIZE = 0xFFFF; //Max basepacket size is 0xFFFF public const int BACKLOG = 100; + public const int HEALTH_THREAD_SLEEP_TIME = 5; + public const string STATIC_ACTORS_PATH = "./staticactors.bin"; private static Server mSelf; @@ -41,6 +43,30 @@ namespace FFXIVClassic_Lobby_Server private PacketProcessor mProcessor; + private Thread mConnectionHealthThread; + private bool killHealthThread = false; + + private void connectionHealth() + { + Log.info(String.Format("Connection Health thread started; it will run every {0} seconds.", HEALTH_THREAD_SLEEP_TIME)); + while (!killHealthThread) + { + lock (mConnectedPlayerList) + { + List dcedPlayers = new List(); + foreach (ConnectedPlayer cp in mConnectedPlayerList.Values) + { + if (cp.checkIfDCing()) + dcedPlayers.Add(cp); + } + + foreach (ConnectedPlayer cp in dcedPlayers) + cp.getActor().cleanupAndSave(); + } + Thread.Sleep(HEALTH_THREAD_SLEEP_TIME * 1000); + } + } + public Server() { mSelf = this; @@ -53,6 +79,10 @@ namespace FFXIVClassic_Lobby_Server public bool startServer() { + mConnectionHealthThread = new Thread(new ThreadStart(connectionHealth)); + mConnectionHealthThread.Name = "MapThread:Health"; + //mConnectionHealthThread.Start(); + mStaticActors = new StaticActors(STATIC_ACTORS_PATH); gamedataItems = Database.getItemGamedata(); @@ -102,6 +132,15 @@ namespace FFXIVClassic_Lobby_Server return true; } + public void removePlayer(Player player) + { + lock (mConnectedPlayerList) + { + if (mConnectedPlayerList.ContainsKey(player.actorId)) + mConnectedPlayerList.Remove(player.actorId); + } + } + #region Socket Handling private void acceptCallback(IAsyncResult result) { @@ -411,17 +450,11 @@ namespace FFXIVClassic_Lobby_Server } } - public LuaEngine GetLuaEngine() - { - return mLuaEngine; - } - public WorldManager GetWorldManager() { return mWorldManager; } - public void printPos(ConnectedPlayer client) { if (client != null) @@ -439,19 +472,36 @@ namespace FFXIVClassic_Lobby_Server } } - private void giveItem(ConnectedPlayer client, uint itemId, int quantity) + private void setGraphic(ConnectedPlayer client, uint slot, uint wId, uint eId, uint vId, uint cId) { if (client != null) { Player p = client.getActor(); - p.getInventory(Inventory.NORMAL).addItem(itemId, quantity, 1); + p.graphicChange(slot, wId, eId, vId, cId); } else { foreach (KeyValuePair entry in mConnectedPlayerList) { Player p = entry.Value.getActor(); - p.getInventory(Inventory.NORMAL).addItem(itemId, quantity, 1); + p.graphicChange(slot, wId, eId, vId, cId); + } + } + } + + private void giveItem(ConnectedPlayer client, uint itemId, int quantity) + { + if (client != null) + { + Player p = client.getActor(); + p.getInventory(Inventory.NORMAL).addItem(itemId, quantity); + } + else + { + foreach (KeyValuePair entry in mConnectedPlayerList) + { + Player p = entry.Value.getActor(); + p.getInventory(Inventory.NORMAL).addItem(itemId, quantity); } } } @@ -463,7 +513,7 @@ namespace FFXIVClassic_Lobby_Server Player p = client.getActor(); if (p.getInventory(type) != null) - p.getInventory(type).addItem(itemId, quantity, 1); + p.getInventory(type).addItem(itemId, quantity); } else { @@ -472,7 +522,7 @@ namespace FFXIVClassic_Lobby_Server Player p = entry.Value.getActor(); if (p.getInventory(type) != null) - p.getInventory(type).addItem(itemId, quantity, 1); + p.getInventory(type).addItem(itemId, quantity); } } } @@ -520,14 +570,14 @@ namespace FFXIVClassic_Lobby_Server if (client != null) { Player p = client.getActor(); - p.getInventory(Inventory.CURRANCY).addItem(itemId, quantity, 1); + p.getInventory(Inventory.CURRANCY).addItem(itemId, quantity); } else { foreach (KeyValuePair entry in mConnectedPlayerList) { Player p = entry.Value.getActor(); - p.getInventory(Inventory.CURRANCY).addItem(itemId, quantity, 1); + p.getInventory(Inventory.CURRANCY).addItem(itemId, quantity); } } } @@ -554,14 +604,14 @@ namespace FFXIVClassic_Lobby_Server if (client != null) { Player p = client.getActor(); - p.getInventory(Inventory.KEYITEMS).addItem(itemId, 1, 1); + p.getInventory(Inventory.KEYITEMS).addItem(itemId, 1); } else { foreach (KeyValuePair entry in mConnectedPlayerList) { Player p = entry.Value.getActor(); - p.getInventory(Inventory.KEYITEMS).addItem(itemId, 1, 1); + p.getInventory(Inventory.KEYITEMS).addItem(itemId, 1); } } } @@ -618,6 +668,18 @@ namespace FFXIVClassic_Lobby_Server mWorldManager.reloadZone(client.getActor().zoneId); return true; } + else if (split[0].Equals("reloaditems")) + { + Log.info(String.Format("Got request to reload item gamedata")); + if (client != null) + client.getActor().queuePacket(SendMessagePacket.buildPacket(client.actorID, client.actorID, SendMessagePacket.MESSAGE_TYPE_GENERAL_INFO, "", "Reloading Item Gamedata...")); + gamedataItems.Clear(); + gamedataItems = Database.getItemGamedata(); + Log.info(String.Format("Loaded {0} items.", gamedataItems.Count)); + if (client != null) + client.getActor().queuePacket(SendMessagePacket.buildPacket(client.actorID, client.actorID, SendMessagePacket.MESSAGE_TYPE_GENERAL_INFO, "", String.Format("Loaded {0} items.", gamedataItems.Count))); + return true; + } else if (split[0].Equals("sendpacket")) { if (split.Length < 2) @@ -633,6 +695,19 @@ namespace FFXIVClassic_Lobby_Server Log.error("Could not load packet: " + e); } } + else if (split[0].Equals("graphic")) + { + try + { + if (split.Length == 6) + setGraphic(client, UInt32.Parse(split[1]), UInt32.Parse(split[2]), UInt32.Parse(split[3]), UInt32.Parse(split[4]), UInt32.Parse(split[5])); + return true; + } + catch (Exception e) + { + Log.error("Could not give item."); + } + } else if (split[0].Equals("giveitem")) { try @@ -755,7 +830,7 @@ namespace FFXIVClassic_Lobby_Server } else if (split[0].Equals("property")) { - if (split.Length == 5) + if (split.Length == 4) testCodePacket(Utils.MurmurHash2(split[1], 0), Convert.ToUInt32(split[2], 16), split[3]); return true; } diff --git a/FFXIVClassic Map Server/WorldManager.cs b/FFXIVClassic Map Server/WorldManager.cs index 89843859..fb54d8aa 100644 --- a/FFXIVClassic Map Server/WorldManager.cs +++ b/FFXIVClassic Map Server/WorldManager.cs @@ -5,6 +5,7 @@ using FFXIVClassic_Map_Server.Actors; using FFXIVClassic_Map_Server.common.EfficientHashTables; using FFXIVClassic_Map_Server.dataobjects; using FFXIVClassic_Map_Server.dataobjects.chara; +using FFXIVClassic_Map_Server.lua; using FFXIVClassic_Map_Server.packets.send; using FFXIVClassic_Map_Server.packets.send.actor; using FFXIVClassic_Map_Server.packets.send.login; @@ -275,7 +276,7 @@ namespace FFXIVClassic_Map_Server if (player.zone != null) { oldZone = player.zone; - oldZone.removeActorToZone(player); + oldZone.removeActorFromZone(player); } //Add player to new zone and update @@ -286,6 +287,8 @@ namespace FFXIVClassic_Map_Server return; newZone.addActorToZone(player); + + LuaEngine.onZoneIn(player); } //Moves actor to new zone, and sends packets to spawn at the given zone entrance @@ -310,7 +313,7 @@ namespace FFXIVClassic_Map_Server if (player.zone != null) { oldZone = player.zone; - oldZone.removeActorToZone(player); + oldZone.removeActorFromZone(player); } //Add player to new zone and update @@ -336,6 +339,8 @@ namespace FFXIVClassic_Map_Server player.sendZoneInPackets(this, spawnType); player.playerSession.clearInstance(); player.sendInstanceUpdate(); + + LuaEngine.onZoneIn(player); } //Login Zone In @@ -351,13 +356,16 @@ namespace FFXIVClassic_Map_Server //Set the current zone and add player player.zone = zone; zone.addActorToZone(player); - + //Send packets player.playerSession.queuePacket(DeleteAllActorsPacket.buildPacket(player.actorId), true, false); player.playerSession.queuePacket(_0x2Packet.buildPacket(player.actorId), true, false); player.sendZoneInPackets(this, 0x1); player.playerSession.clearInstance(); player.sendInstanceUpdate(); + + LuaEngine.onLogin(player); + LuaEngine.onZoneIn(player); } public void reloadZone(uint zoneId) diff --git a/FFXIVClassic Map Server/actors/area/Area.cs b/FFXIVClassic Map Server/actors/area/Area.cs index 7a189dae..15102ac5 100644 --- a/FFXIVClassic Map Server/actors/area/Area.cs +++ b/FFXIVClassic Map Server/actors/area/Area.cs @@ -4,6 +4,7 @@ using FFXIVClassic_Map_Server.dataobjects; using FFXIVClassic_Map_Server.dataobjects.chara; using FFXIVClassic_Map_Server.lua; using FFXIVClassic_Map_Server.packets.send.actor; +using MoonSharp.Interpreter; using System; using System.Collections.Generic; using System.Linq; @@ -20,7 +21,6 @@ namespace FFXIVClassic_Map_Server.Actors public ushort weatherNormal, weatherCommon, weatherRare; public ushort bgmDay, bgmNight, bgmBattle; - private string classPath; public int boundingGridSize = 50; @@ -31,6 +31,8 @@ namespace FFXIVClassic_Map_Server.Actors private Dictionary mActorList = new Dictionary(); private List[,] mActorBlock; + Script areaScript; + public Area(uint id, string zoneName, ushort regionId, string className, ushort bgmDay, ushort bgmNight, ushort bgmBattle, bool isIsolated, bool isInn, bool canRideChocobo, bool canStealth, bool isInstanceRaid) : base(id) { @@ -66,7 +68,7 @@ namespace FFXIVClassic_Map_Server.Actors mActorBlock[x, y] = new List(); } } - + } public override SubPacket createScriptBindPacket(uint playerActorId) @@ -117,7 +119,7 @@ namespace FFXIVClassic_Map_Server.Actors mActorBlock[gridX, gridY].Add(actor); } - public void removeActorToZone(Actor actor) + public void removeActorFromZone(Actor actor) { mActorList.Remove(actor.actorId); diff --git a/FFXIVClassic Map Server/actors/chara/Character.cs b/FFXIVClassic Map Server/actors/chara/Character.cs index bf9fba7f..8ada9422 100644 --- a/FFXIVClassic Map Server/actors/chara/Character.cs +++ b/FFXIVClassic Map Server/actors/chara/Character.cs @@ -16,29 +16,32 @@ namespace FFXIVClassic_Map_Server.Actors public const int FACEINFO = 2; public const int HIGHLIGHT_HAIR = 3; public const int VOICE = 4; - public const int WEAPON1 = 5; - public const int WEAPON2 = 6; - public const int WEAPON3 = 7; - public const int UNKNOWN1 = 8; - public const int UNKNOWN2 = 9; - public const int UNKNOWN3 = 10; - public const int UNKNOWN4 = 11; + public const int MAINHAND = 5; + public const int OFFHAND = 6; + public const int SPMAINHAND = 7; + public const int SPOFFHAND = 8; + public const int THROWING = 9; + public const int PACK = 10; + public const int POUCH = 11; public const int HEADGEAR = 12; public const int BODYGEAR = 13; public const int LEGSGEAR = 14; public const int HANDSGEAR = 15; public const int FEETGEAR = 16; public const int WAISTGEAR = 17; - public const int UNKNOWN5 = 18; - public const int R_EAR = 19; - public const int L_EAR = 20; - public const int UNKNOWN6 = 21; - public const int UNKNOWN7 = 22; - public const int R_FINGER = 23; - public const int L_FINGER = 24; + public const int NECKGEAR = 18; + public const int L_EAR = 19; + public const int R_EAR = 20; + public const int R_WRIST = 21; + public const int L_WRIST = 22; + public const int R_RINGFINGER = 23; + public const int L_RINGFINGER = 24; + public const int R_INDEXFINGER = 25; + public const int L_INDEXFINGER = 26; + public const int UNKNOWN = 27; public uint modelId; - public uint[] appearanceIds = new uint[0x1D]; + public uint[] appearanceIds = new uint[28]; public uint animationId = 0; diff --git a/FFXIVClassic Map Server/actors/chara/npc/Npc.cs b/FFXIVClassic Map Server/actors/chara/npc/Npc.cs index 310f5828..70eed95f 100644 --- a/FFXIVClassic Map Server/actors/chara/npc/Npc.cs +++ b/FFXIVClassic Map Server/actors/chara/npc/Npc.cs @@ -49,9 +49,8 @@ namespace FFXIVClassic_Map_Server.Actors { List lParams; - LuaEngine lua = Server.getServer().GetLuaEngine(); Player player = Server.getServer().GetWorldManager().GetPCInWorld(playerActorId); - lParams = lua.doActorOnInstantiate(player, this); + lParams = LuaEngine.doActorOnInstantiate(player, this); if (lParams == null) { @@ -154,7 +153,7 @@ namespace FFXIVClassic_Map_Server.Actors appearanceIds[Character.FACEINFO] = PrimitiveConversion.ToUInt32(CharacterUtils.getFaceInfo(reader.GetByte(6), reader.GetByte(7), reader.GetByte(5), reader.GetByte(14), reader.GetByte(13), reader.GetByte(12), reader.GetByte(11), reader.GetByte(10), reader.GetByte(9), reader.GetByte(8))); appearanceIds[Character.HIGHLIGHT_HAIR] = (uint)(reader.GetUInt32(3) | reader.GetUInt32(2) << 10); //5- Hair Highlight, 4 - Hair Style appearanceIds[Character.VOICE] = reader.GetUInt32(17); - appearanceIds[Character.WEAPON1] = reader.GetUInt32(19); + appearanceIds[Character.MAINHAND] = reader.GetUInt32(19); //appearanceIds[Character.WEAPON2] = reader.GetUInt32(22); appearanceIds[Character.HEADGEAR] = reader.GetUInt32(26); appearanceIds[Character.BODYGEAR] = reader.GetUInt32(27); @@ -164,8 +163,8 @@ namespace FFXIVClassic_Map_Server.Actors appearanceIds[Character.WAISTGEAR] = reader.GetUInt32(31); appearanceIds[Character.R_EAR] = reader.GetUInt32(32); appearanceIds[Character.L_EAR] = reader.GetUInt32(33); - appearanceIds[Character.R_FINGER] = reader.GetUInt32(36); - appearanceIds[Character.L_FINGER] = reader.GetUInt32(37); + appearanceIds[Character.R_RINGFINGER] = reader.GetUInt32(36); + appearanceIds[Character.L_RINGFINGER] = reader.GetUInt32(37); } } diff --git a/FFXIVClassic Map Server/actors/chara/player/Equipment.cs b/FFXIVClassic Map Server/actors/chara/player/Equipment.cs index 0d0955fc..5543f154 100644 --- a/FFXIVClassic Map Server/actors/chara/player/Equipment.cs +++ b/FFXIVClassic Map Server/actors/chara/player/Equipment.cs @@ -13,24 +13,24 @@ namespace FFXIVClassic_Map_Server.actors.chara.player { class Equipment { - public const int SLOT_MAINHAND = 0x00; - public const int SLOT_OFFHAND = 0x01; - public const int SLOT_THROWINGWEAPON = 0x04; - public const int SLOT_PACK = 0x05; - public const int SLOT_POUCH = 0x06; - public const int SLOT_HEAD = 0x08; - public const int SLOT_UNDERSHIRT = 0x09; - public const int SLOT_BODY = 0x0A; - public const int SLOT_UNDERGARMENT = 0x0B; - public const int SLOT_LEGS = 0x0C; - public const int SLOT_HANDS = 0x0D; - public const int SLOT_BOOTS = 0x0E; - public const int SLOT_WAIST = 0x0F; - public const int SLOT_NECK = 0x10; - public const int SLOT_EARS = 0x11; - public const int SLOT_WRISTS = 0x13; - public const int SLOT_RIGHTFINGER = 0x15; - public const int SLOT_LEFTFINGER = 0x16; + public const int SLOT_MAINHAND = 0; + public const int SLOT_OFFHAND = 1; + public const int SLOT_THROWINGWEAPON = 4; + public const int SLOT_PACK = 5; + public const int SLOT_POUCH = 6; + public const int SLOT_HEAD = 8; + public const int SLOT_UNDERSHIRT = 9; + public const int SLOT_BODY = 10; + public const int SLOT_UNDERGARMENT = 11; + public const int SLOT_LEGS = 12; + public const int SLOT_HANDS = 13; + public const int SLOT_BOOTS = 14; + public const int SLOT_WAIST = 15; + public const int SLOT_NECK = 16; + public const int SLOT_EARS = 17; + public const int SLOT_WRISTS = 19; + public const int SLOT_RIGHTFINGER = 21; + public const int SLOT_LEFTFINGER = 22; private Player owner; private ushort inventoryCapacity; @@ -71,6 +71,27 @@ namespace FFXIVClassic_Map_Server.actors.chara.player owner.queuePacket(InventorySetEndPacket.buildPacket(owner.actorId)); } + public void SetEquipment(ushort[] slots, ushort[] itemSlots) + { + if (slots.Length != itemSlots.Length) + return; + + for (int i = 0; i < slots.Length; i++) + { + InventoryItem item = normalInventory.getItem(itemSlots[i]); + + if (item == null) + continue; + + Database.equipItem(owner, slots[i], itemSlots[i]); + list[slots[i]] = normalInventory.getItem(itemSlots[i]); + } + + owner.queuePacket(InventoryBeginChangePacket.buildPacket(owner.actorId)); + SendFullEquipment(false); + owner.queuePacket(InventoryEndChangePacket.buildPacket(owner.actorId)); + } + public void SetEquipment(List> toEquip) { List slotsToUpdate = new List(); @@ -81,6 +102,16 @@ namespace FFXIVClassic_Map_Server.actors.chara.player } } + public void Equip(ushort slot, ushort invSlot) + { + InventoryItem item = normalInventory.getItem(invSlot); + + if (item == null) + return; + + Equip(slot, item); + } + public void Equip(ushort slot, InventoryItem item) { if (slot >= list.Length) diff --git a/FFXIVClassic Map Server/actors/chara/player/Inventory.cs b/FFXIVClassic Map Server/actors/chara/player/Inventory.cs index a331a950..869dd436 100644 --- a/FFXIVClassic Map Server/actors/chara/player/Inventory.cs +++ b/FFXIVClassic Map Server/actors/chara/player/Inventory.cs @@ -70,6 +70,16 @@ namespace FFXIVClassic_Map_Server.actors.chara.player owner.queuePacket(InventorySetEndPacket.buildPacket(owner.actorId)); } + public void addItem(uint itemId) + { + addItem(itemId, 1, 1); + } + + public void addItem(uint itemId, int quantity) + { + addItem(itemId, quantity, 1); + } + public void addItem(uint itemId, int quantity, byte quality) { if (!isSpaceForAdd(itemId, quantity)) @@ -119,7 +129,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.player //New item that spilled over while (quantityCount > 0) { - InventoryItem addedItem = Database.addItem(owner, itemId, Math.Min(quantityCount, 5), quality, gItem.isExclusive ? (byte)0x3 : (byte)0x0, gItem.durability, inventoryCode); + InventoryItem addedItem = Database.addItem(owner, itemId, Math.Min(quantityCount, gItem.maxStack), quality, gItem.isExclusive ? (byte)0x3 : (byte)0x0, gItem.durability, inventoryCode); list.Add(addedItem); @@ -137,6 +147,31 @@ namespace FFXIVClassic_Map_Server.actors.chara.player owner.queuePacket(InventoryEndChangePacket.buildPacket(owner.actorId)); } + public void addItem(uint[] itemId) + { + if (!isSpaceForAdd(itemId[0], itemId.Length)) + return; + + //Update lists and db + owner.queuePacket(InventoryBeginChangePacket.buildPacket(owner.actorId)); + owner.queuePacket(InventorySetBeginPacket.buildPacket(owner.actorId, inventoryCapacity, inventoryCode)); + + int startPos = list.Count; + + //New item that spilled over + for (int i = 0; i < itemId.Length; i++) + { + Item gItem = Server.getItemGamedata(itemId[i]); + InventoryItem addedItem = Database.addItem(owner, itemId[i], 1, (byte)1, gItem.isExclusive ? (byte)0x3 : (byte)0x0, gItem.durability, inventoryCode); + list.Add(addedItem); + } + + sendInventoryPackets(startPos); + + owner.queuePacket(InventorySetEndPacket.buildPacket(owner.actorId)); + owner.queuePacket(InventoryEndChangePacket.buildPacket(owner.actorId)); + } + public void removeItem(uint itemId, int quantity) { if (!hasItem(itemId, quantity)) diff --git a/FFXIVClassic Map Server/actors/chara/player/Player.cs b/FFXIVClassic Map Server/actors/chara/player/Player.cs index 15c726d6..7ed6cf47 100644 --- a/FFXIVClassic Map Server/actors/chara/player/Player.cs +++ b/FFXIVClassic Map Server/actors/chara/player/Player.cs @@ -59,6 +59,9 @@ namespace FFXIVClassic_Map_Server.Actors public uint[] timers = new uint[20]; public ushort currentJob; public uint currentTitle; + public uint playTime; + public uint lastPlayTimeUpdate; + public bool isGM = false; //Inventory private Dictionary inventories = new Dictionary(); @@ -162,6 +165,7 @@ namespace FFXIVClassic_Map_Server.Actors charaWork.parameterTemp.tp = 3000; Database.loadPlayerCharacter(this); + lastPlayTimeUpdate = Utils.UnixTimeStampUTC(); } public List create0x132Packets(uint playerActorId) @@ -545,6 +549,34 @@ namespace FFXIVClassic_Map_Server.Actors } } + public void setDCFlag(bool flag) + { + if (flag) + { + broadcastPacket(SetActorIconPacket.buildPacket(actorId, actorId, SetActorIconPacket.DISCONNECTING), true); + } + else + { + if (isGM) + broadcastPacket(SetActorIconPacket.buildPacket(actorId, actorId, SetActorIconPacket.ISGM), true); + else + broadcastPacket(SetActorIconPacket.buildPacket(actorId, actorId, 0), true); + } + } + + public void cleanupAndSave() + { + //Remove actor from zone and main server list + zone.removeActorFromZone(this); + Server.getServer().removePlayer(this); + + //Save Player + Database.savePlayerPlayTime(this); + Database.savePlayerPosition(this); + + Log.info(String.Format("{0} has been logged out and saved.", this.customDisplayName)); + } + public Zone getZone() { return zone; @@ -558,11 +590,25 @@ namespace FFXIVClassic_Map_Server.Actors public void logout() { queuePacket(LogoutPacket.buildPacket(actorId)); + cleanupAndSave(); } public void quitGame() { queuePacket(QuitPacket.buildPacket(actorId)); + cleanupAndSave(); + } + + public uint getPlayTime(bool doUpdate) + { + if (doUpdate) + { + uint curTime = Utils.UnixTimeStampUTC(); + playTime += curTime - lastPlayTimeUpdate; + lastPlayTimeUpdate = curTime; + } + + return playTime; } public void changeMusic(ushort musicId) @@ -635,33 +681,62 @@ namespace FFXIVClassic_Map_Server.Actors //zone.broadcastPacketAroundActor(this, worldMasterMessage); } + public void graphicChange(uint slot, uint graphicId) + { + appearanceIds[slot] = graphicId; + broadcastPacket(createAppearancePacket(actorId), true); + } + + public void graphicChange(uint slot, uint weapId, uint equipId, uint variantId, uint colorId) + { + + uint mixedVariantId; + + if (weapId == 0) + mixedVariantId = ((variantId & 0x1F) << 5) | colorId; + else + mixedVariantId = variantId; + + uint graphicId = + (weapId & 0x3FF) << 20 | + (equipId & 0x3FF) << 10 | + (mixedVariantId & 0x3FF); + + appearanceIds[slot] = graphicId; + broadcastPacket(createAppearancePacket(actorId), true); + + } + public void graphicChange(int slot, InventoryItem invItem) { - Item item = Server.getItemGamedata(invItem.itemId); - - if (item == null) + if (invItem == null) + appearanceIds[slot] = 0; + else { - - } - else if (item is EquipmentItem) - { - EquipmentItem eqItem = (EquipmentItem)item; - - uint graphicId; - - if (eqItem.graphicsWeaponId == null || eqItem.graphicsEquipmentId == null || eqItem.graphicsVariantId == null) - graphicId = 1025; - else + Item item = Server.getItemGamedata(invItem.itemId); + if (item is EquipmentItem) { - graphicId = - eqItem.graphicsWeaponId << 20 | - eqItem.graphicsEquipmentId << 10 | - eqItem.graphicsVariantId << 5 | - eqItem.graphicsColorId << 5; + EquipmentItem eqItem = (EquipmentItem)item; + + uint mixedVariantId; + + if (eqItem.graphicsWeaponId == 0) + mixedVariantId = ((eqItem.graphicsVariantId & 0x1F) << 5) | eqItem.graphicsColorId; + else + mixedVariantId = eqItem.graphicsVariantId; + + uint graphicId = + (eqItem.graphicsWeaponId & 0x3FF) << 20 | + (eqItem.graphicsEquipmentId & 0x3FF) << 10 | + (mixedVariantId & 0x3FF); + + appearanceIds[slot] = graphicId; } - appearanceIds[BODYGEAR] = graphicId; - broadcastPacket(createAppearancePacket(actorId), true); - } + } + + Database.savePlayerAppearance(this); + + broadcastPacket(createAppearancePacket(actorId), true); } public Inventory getInventory(ushort type) diff --git a/FFXIVClassic Map Server/common/Log.cs b/FFXIVClassic Map Server/common/Log.cs index 93ab8533..5c127a25 100644 --- a/FFXIVClassic Map Server/common/Log.cs +++ b/FFXIVClassic Map Server/common/Log.cs @@ -18,12 +18,14 @@ namespace FFXIVClassic_Lobby_Server.common } public static void debug(String message) - { + { +#if DEBUG Console.Write("[{0}]", DateTime.Now.ToString("dd/MMM HH:mm")); Console.ForegroundColor = ConsoleColor.Yellow; Console.Write("[DEBUG] "); Console.ForegroundColor = ConsoleColor.Gray; Console.WriteLine(message); +#endif } public static void info(String message) diff --git a/FFXIVClassic Map Server/common/Utils.cs b/FFXIVClassic Map Server/common/Utils.cs index eb7ea239..d66c188a 100644 --- a/FFXIVClassic Map Server/common/Utils.cs +++ b/FFXIVClassic Map Server/common/Utils.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; @@ -179,5 +180,127 @@ namespace FFXIVClassic_Lobby_Server.common return data; } + + public static string FFXIVLoginStringDecodeBinary(string path) + { + Console.OutputEncoding = System.Text.Encoding.UTF8; + byte[] data = File.ReadAllBytes(path); + int offset = 0x5405a; + //int offset = 0x5425d; + //int offset = 0x53ea0; + while (true) + { + string result = ""; + uint key = (uint)data[offset + 0] << 8 | data[offset+1]; + uint key2 = data[offset + 2]; + key = RotateRight(key, 1) & 0xFFFF; + key -= 0x22AF; + key &= 0xFFFF; + key2 = key2 ^ key; + key = RotateRight(key, 1) & 0xFFFF; + key -= 0x22AF; + key &= 0xFFFF; + uint finalKey = key; + key = data[offset + 3]; + uint count = (key2 & 0xFF) << 8; + key = key ^ finalKey; + key &= 0xFF; + count |= key; + + int count2 = 0; + while (count != 0) + { + uint encrypted = data[offset + 4 + count2]; + finalKey = RotateRight(finalKey, 1) & 0xFFFF; + finalKey -= 0x22AF; + finalKey &= 0xFFFF; + encrypted = encrypted ^ (finalKey & 0xFF); + + result += (char)encrypted; + count--; + count2++; + } + + offset += 4 + count2; + + Console.WriteLine(result); + } + } + + public static string FFXIVLoginStringDecode(byte[] data) + { + string result = ""; + uint key = (uint)data[0] << 8 | data[1]; + uint key2 = data[2]; + key = RotateRight(key, 1) & 0xFFFF; + key -= 0x22AF; + key2 = key2 ^ key; + key = RotateRight(key, 1) & 0xFFFF; + key -= 0x22AF; + uint finalKey = key; + key = data[3]; + uint count = (key2 & 0xFF) << 8; + key = key ^ finalKey; + key &= 0xFF; + count |= key; + + int count2 = 0; + while (count != 0) + { + uint encrypted = data[4 + count2]; + finalKey = RotateRight(finalKey, 1) & 0xFFFF; + finalKey -= 0x22AF; + encrypted = encrypted ^ (finalKey & 0xFF); + result += (char)encrypted; + count--; + count2++; + } + + return result; + } + + public static byte[] FFXIVLoginStringEncode(uint key, string text) + { + key = key & 0xFFFF; + + uint count = 0; + byte[] asciiBytes = Encoding.ASCII.GetBytes(text); + byte[] result = new byte[4 + text.Length]; + for (count = 0; count < text.Length; count++) + { + result[result.Length - count - 1] = (byte)(asciiBytes[asciiBytes.Length - count - 1] ^ (key & 0xFF)); + key += 0x22AF; + key &= 0xFFFF; + key = RotateLeft(key, 1) & 0xFFFF; + } + + count = count ^ key; + result[3] = (byte) (count & 0xFF); + + key += 0x22AF & 0xFFFF; + key = RotateLeft(key, 1) & 0xFFFF; + + result[2] = (byte)(key & 0xFF); + + key += 0x22AF & 0xFFFF; + key = RotateLeft(key, 1) & 0xFFFF; + + + result[1] = (byte)(key & 0xFF); + result[0] = (byte)((key >> 8) & 0xFF); + + return result; + } + + public static uint RotateLeft(uint value, int bits) + { + return (value << bits) | (value >> (16 - bits)); + } + + public static uint RotateRight(uint value, int bits) + { + return (value >> bits) | (value << (16 - bits)); + } + } } diff --git a/FFXIVClassic Map Server/dataobjects/ConnectedPlayer.cs b/FFXIVClassic Map Server/dataobjects/ConnectedPlayer.cs index c90ee4a8..c77123c7 100644 --- a/FFXIVClassic Map Server/dataobjects/ConnectedPlayer.cs +++ b/FFXIVClassic Map Server/dataobjects/ConnectedPlayer.cs @@ -1,4 +1,5 @@ using FFXIVClassic_Lobby_Server; +using FFXIVClassic_Lobby_Server.common; using FFXIVClassic_Lobby_Server.packets; using FFXIVClassic_Map_Server.Actors; using FFXIVClassic_Map_Server.packets.send.actor; @@ -19,9 +20,9 @@ namespace FFXIVClassic_Map_Server.dataobjects private ClientConnection zoneConnection; private ClientConnection chatConnection; - public string errorMessage = ""; + private uint lastPingPacket = Utils.UnixTimeStampUTC(); - bool isDisconnected; + public string errorMessage = ""; public ConnectedPlayer(uint actorId) { @@ -51,11 +52,15 @@ namespace FFXIVClassic_Map_Server.dataobjects public void disconnect() { - isDisconnected = true; zoneConnection.disconnect(); chatConnection.disconnect(); } + public bool isDisconnected() + { + return (!zoneConnection.isConnected() && !chatConnection.isConnected()); + } + public void queuePacket(BasePacket basePacket) { zoneConnection.queuePacket(basePacket); @@ -71,6 +76,23 @@ namespace FFXIVClassic_Map_Server.dataobjects return playerActor; } + public void ping() + { + lastPingPacket = Utils.UnixTimeStampUTC(); + } + + public bool checkIfDCing() + { + uint currentTime = Utils.UnixTimeStampUTC(); + if (currentTime - lastPingPacket >= 5000) //Show D/C flag + playerActor.setDCFlag(true); + else if (currentTime - lastPingPacket >= 30000) //DCed + return true; + else + playerActor.setDCFlag(false); + return false; + } + public void updatePlayerActorPosition(float x, float y, float z, float rot, ushort moveState) { @@ -88,17 +110,7 @@ namespace FFXIVClassic_Map_Server.dataobjects getActor().zone.updateActorPosition(getActor()); } - - public void sendMotd() - { - - } - - public void sendChat(ConnectedPlayer sender, string message, int mode) - { - - } - + public List updateInstance(List list) { List basePackets = new List(); diff --git a/FFXIVClassic Map Server/lua/LuaEngine.cs b/FFXIVClassic Map Server/lua/LuaEngine.cs index bacab91a..387b9c05 100644 --- a/FFXIVClassic Map Server/lua/LuaEngine.cs +++ b/FFXIVClassic Map Server/lua/LuaEngine.cs @@ -19,6 +19,8 @@ namespace FFXIVClassic_Map_Server.lua { class LuaEngine { + const string FILEPATH_PLAYER = "./scripts/player.lua"; + const string FILEPATH_ZONE = "./scripts/zones/{0}/zone.lua"; const string FILEPATH_COMMANDS = "./scripts/commands/{0}.lua"; const string FILEPATH_NPCS = "./scripts/zones/{0}/npcs/{1}.lua"; @@ -27,7 +29,7 @@ namespace FFXIVClassic_Map_Server.lua UserData.RegistrationPolicy = InteropRegistrationPolicy.Automatic; } - public List doActorOnInstantiate(Player player, Actor target) + public static List doActorOnInstantiate(Player player, Actor target) { string luaPath; @@ -58,7 +60,7 @@ namespace FFXIVClassic_Map_Server.lua return null; } - public void doActorOnEventStarted(Player player, Actor target, EventStartPacket eventStart) + public static void doActorOnEventStarted(Player player, Actor target, EventStartPacket eventStart) { string luaPath; @@ -96,7 +98,7 @@ namespace FFXIVClassic_Map_Server.lua } - public void doActorOnEventUpdated(Player player, Actor target, EventUpdatePacket eventUpdate) + public static void doActorOnEventUpdated(Player player, Actor target, EventUpdatePacket eventUpdate) { string luaPath; @@ -131,5 +133,38 @@ namespace FFXIVClassic_Map_Server.lua player.playerSession.queuePacket(BasePacket.createPacket(sendError, true, false)); } } + + public static void onZoneIn(Player player) + { + string luaPath = String.Format(FILEPATH_ZONE, player.getZone().actorId); + + if (File.Exists(luaPath)) + { + Script script = new Script(); + ((ScriptLoaderBase)script.Options.ScriptLoader).ModulePaths = new string[] { "./scripts/?", "./scripts/?.lua" }; + script.Globals["getStaticActor"] = (Func)Server.getStaticActors; + script.Globals["getWorldMaster"] = (Func)Server.getServer().GetWorldManager().GetActor; + script.DoFile(luaPath); + + //Run Script + DynValue result = script.Call(script.Globals["onZoneIn"], player); + } + } + + public static void onLogin(Player player) + { + if (File.Exists(FILEPATH_PLAYER)) + { + Script script = new Script(); + ((ScriptLoaderBase)script.Options.ScriptLoader).ModulePaths = new string[] { "./scripts/?", "./scripts/?.lua" }; + script.Globals["getStaticActor"] = (Func)Server.getStaticActors; + script.Globals["getWorldMaster"] = (Func)Server.getServer().GetWorldManager().GetActor; + script.DoFile(FILEPATH_PLAYER); + + //Run Script + DynValue result = script.Call(script.Globals["onLogin"], player); + } + } + } } diff --git a/FFXIVClassic Map Server/packets/send/Actor/SetActorAppearancePacket.cs b/FFXIVClassic Map Server/packets/send/Actor/SetActorAppearancePacket.cs index 51643953..cca658af 100644 --- a/FFXIVClassic Map Server/packets/send/Actor/SetActorAppearancePacket.cs +++ b/FFXIVClassic Map Server/packets/send/Actor/SetActorAppearancePacket.cs @@ -18,22 +18,29 @@ namespace FFXIVClassic_Map_Server.packets.send.actor public const int FACEINFO = 2; public const int HIGHLIGHT_HAIR = 3; public const int VOICE = 4; - public const int WEAPON1 = 5; - public const int WEAPON2 = 6; - public const int WEAPON3 = 7; + public const int MAINHAND = 5; + public const int OFFHAND = 6; + public const int SPMAINHAND = 7; + public const int SPOFFHAND = 8; + public const int THROWING = 9; + public const int PACK = 10; + public const int POUCH = 11; public const int HEADGEAR = 12; public const int BODYGEAR = 13; public const int LEGSGEAR = 14; public const int HANDSGEAR = 15; public const int FEETGEAR = 16; public const int WAISTGEAR = 17; - public const int UNKNOWN1 = 18; - public const int R_EAR = 19; - public const int L_EAR = 20; - public const int UNKNOWN2 = 21; - public const int UNKNOWN3 = 22; - public const int R_FINGER = 23; - public const int L_FINGER = 24; + public const int NECKGEAR = 18; + public const int L_EAR = 19; + public const int R_EAR = 20; + public const int R_WRIST = 21; + public const int L_WRIST = 22; + public const int R_RINGFINGER = 23; + public const int L_RINGFINGER = 24; + public const int R_INDEXFINGER = 25; + public const int L_INDEXFINGER = 26; + public const int UNKNOWN = 27; public uint modelID; public uint[] appearanceIDs; @@ -41,7 +48,7 @@ namespace FFXIVClassic_Map_Server.packets.send.actor public SetActorAppearancePacket(uint modelID) { this.modelID = modelID; - appearanceIDs = new uint[0x1D]; + appearanceIDs = new uint[28]; } public SetActorAppearancePacket(uint modelID, uint[] appearanceTable) @@ -58,16 +65,15 @@ namespace FFXIVClassic_Map_Server.packets.send.actor { using (BinaryWriter binWriter = new BinaryWriter(mem)) { - binWriter.Write((uint)modelID); - for (int i = 0; i <= 0x1A; i++) + binWriter.Write((uint)modelID); + for (int i = 0; i < appearanceIDs.Length; i++) { binWriter.Write((uint)i); binWriter.Write((uint)appearanceIDs[i]); } - binWriter.Write((uint) 0x1B); - binWriter.Seek(0x20, SeekOrigin.Current); - binWriter.Write((uint) 0x1C); - binWriter.Write((uint) 0x00); + + binWriter.Seek(0x100, SeekOrigin.Begin); + binWriter.Write(appearanceIDs.Length); } }