diff --git a/Common Class Lib/Bitstream.cs b/Common Class Lib/Bitstream.cs index a543a75c..1e3df17c 100644 --- a/Common Class Lib/Bitstream.cs +++ b/Common Class Lib/Bitstream.cs @@ -9,7 +9,7 @@ namespace Meteor.Common { public class Bitstream { - private readonly byte[] Data; + private byte[] Data; public Bitstream(uint numBits, bool setAllTrue = false) { @@ -51,6 +51,12 @@ namespace Meteor.Common } } + public void SetTo(bool[] result) + { + Debug.Assert(Data.Length == result.Length / 8); + Data = Utils.ConvertBoolArrayToBinaryStream(result); + } + public bool Get(uint at) { return Get((int)at); @@ -60,6 +66,7 @@ namespace Meteor.Common { int bytePos = at / 8; int bitPos = at % 8; + return (Data[bytePos] & (1 << bitPos)) != 0; } @@ -146,5 +153,46 @@ namespace Meteor.Common return Data; } + public byte[] GetSlice(ushort from, ushort to) + { + int remainder = ((to - from) % 8 != 0) ? 1 : 0; + byte[] toReturn = new byte[((to - from) / 8) + remainder + 1]; + toReturn[toReturn.Length - 1] = 0x3; + + + byte curByte = 0; + + int destByteIndx = 0; + int destShiftIndx = 0; + int srcByteIndx = from / 8; + int srcShiftIndx = from % 8; + + for (int i = from; i <= to; i++) + { + // Skip Zeros + if (Data[srcByteIndx] == 0) + { + srcByteIndx++; + srcShiftIndx = 0; + destByteIndx++; + i += 8; + } + + bool val = (Data[srcByteIndx] & (1 << srcShiftIndx)) != 0; + + curByte |= (byte)((val ? 1 : 0) << destShiftIndx++); + if (destShiftIndx == 8) + { + toReturn[destByteIndx++] = curByte; + destShiftIndx = 0; + curByte = 0; + } + } + + if (destByteIndx == toReturn.Length - 2) + toReturn[destByteIndx] = curByte; + + return toReturn; + } } } diff --git a/Common Class Lib/Utils.cs b/Common Class Lib/Utils.cs index 9b52009c..9556e8c5 100644 --- a/Common Class Lib/Utils.cs +++ b/Common Class Lib/Utils.cs @@ -255,6 +255,26 @@ namespace Meteor.Common return data; } + public static bool[] ConvertBinaryStreamToBoolArray(byte[] bytes) + { + bool[] data = new bool[bytes.Length * 8]; + + int boolCounter = 0; + for (int i = 0; i < bytes.Length; i ++) + { + if (bytes[i] == 0) + { + boolCounter += 8; + continue; + } + + for (int bitCount = 0; bitCount < 8; bitCount++) + data[boolCounter++] = (bytes[i] >> bitCount & 1) == 1; + } + + return data; + } + public static string ToStringBase63(int number) { var lookup = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; diff --git a/Data/scripts/quests/etc/etc3g0.lua b/Data/scripts/quests/etc/etc3g0.lua index cacb5111..e8d333ae 100644 --- a/Data/scripts/quests/etc/etc3g0.lua +++ b/Data/scripts/quests/etc/etc3g0.lua @@ -74,7 +74,7 @@ function onTalk(player, quest, npc, eventName) -- Offer the quest if (npcClassId == KINNISON and not player:HasQuest(quest)) then local questAccepted = callClientFunction(player, "delegateEvent", player, quest, "processEventOffersStart"); - if (questAccepted) then + if (questAccepted == 1) then player:AcceptQuest(quest); end player:EndEvent(); @@ -146,7 +146,7 @@ function onTalk(player, quest, npc, eventName) if (npcClassId == KINNISON) then callClientFunction(player, "delegateEvent", player, quest, "processEventClear"); callClientFunction(player, "delegateEvent", player, quest, "sqrwa", 200, 1, 1, 9); - player:CompleteQuest(quest:GetQuestId()); + player:CompleteQuest(quest); end end quest:UpdateENPCs(); diff --git a/Map Server/Actors/Chara/Player/Player.cs b/Map Server/Actors/Chara/Player/Player.cs index b1ae6fc5..e67e1379 100644 --- a/Map Server/Actors/Chara/Player/Player.cs +++ b/Map Server/Actors/Chara/Player/Player.cs @@ -47,6 +47,7 @@ using Meteor.Map.packets.send.actor.battle; using Meteor.Map.packets.receive.events; using static Meteor.Map.LuaUtils; using Meteor.Map.packets.send.actor.events; +using System.Text; namespace Meteor.Map.Actors { @@ -243,8 +244,7 @@ namespace Meteor.Map.Actors charaWork.command[15] = 0xA0F00000 | 22015; charaWork.commandAcquired[27150 - 26000] = true; - - playerWork.questScenarioComplete[110001 - 110001] = true; + playerWork.questGuildleveComplete[120050 - 120001] = true; for (int i = 0; i < charaWork.additionalCommandAcquired.Length; i++ ) @@ -278,7 +278,7 @@ namespace Meteor.Map.Actors CalculateBaseStats(); questStateManager = new QuestStateManager(this); - questStateManager.Init(questScenario); + questStateManager.Init(questScenario, playerWork.questScenarioComplete); } public List Create0x132Packets() @@ -1157,6 +1157,44 @@ namespace Meteor.Map.Actors } + private void SendAchievedAetheryte(ushort from, ushort to) + { + Bitstream fakeAetheryte = new Bitstream(512, true); + + SetActorPropetyPacket completedQuestWorkUpdate = new SetActorPropetyPacket(from, to, "work/achieveAetheryte"); + completedQuestWorkUpdate.AddBitfield(Utils.MurmurHash2("work.event_achieve_aetheryte", 0), fakeAetheryte.GetSlice(from, to)); + completedQuestWorkUpdate.AddTarget(); + QueuePacket(completedQuestWorkUpdate.BuildPacket(Id)); + } + + private void SendCompletedQuests(ushort from, ushort to) + { + Bitstream completed = questStateManager.GetCompletedBitstream(); + completed.SetAll(true); + byte[] data = completed.GetSlice(from, to); + + SetActorPropetyPacket completedQuestWorkUpdate = new SetActorPropetyPacket(from, to, "playerWork/journal"); + completedQuestWorkUpdate.AddBitfield(Utils.MurmurHash2("playerWork.questScenarioComplete", 0), data); + completedQuestWorkUpdate.AddTarget(); + QueuePacket(completedQuestWorkUpdate.BuildPacket(Id)); + } + + public void OnWorkSyncRequest(string propertyName, ushort from = 0, ushort to = 0) + { + switch (propertyName) + { + case "charaWork/exp": + SendCharaExpInfo(); + break; + case "work/achieveAetheryte": + SendAchievedAetheryte(from, to); + break; + case "playerWork/questCompleteS": + SendCompletedQuests(from, to); + break; + } + } + public int GetHighestLevel() { int max = 0; @@ -1436,7 +1474,6 @@ namespace Meteor.Map.Actors playerWork.questScenario[freeSlot] = instance.Id; questScenario[freeSlot] = instance; - Database.SaveQuest(this, questScenario[freeSlot], freeSlot); SendQuestClientUpdate(freeSlot); if (!isSilent) @@ -1446,6 +1483,8 @@ namespace Meteor.Map.Actors instance.OnAccept(); + Database.SaveQuest(this, questScenario[freeSlot], freeSlot); + return true; } @@ -1458,8 +1497,11 @@ namespace Meteor.Map.Actors { questScenario[i] = newQuestInstance; playerWork.questScenario[i] = questScenario[i].Id; - Database.SaveQuest(this, questScenario[i], i); SendQuestClientUpdate(i); + oldQuestInstance.OnComplete(); + questStateManager.UpdateQuestCompleted(oldQuestInstance); + newQuestInstance.OnAccept(); + Database.SaveQuest(this, questScenario[i], i); break; } } @@ -1472,14 +1514,14 @@ namespace Meteor.Map.Actors { // Remove the quest from the DB and update client work values playerWork.questScenarioComplete[completed.GetQuestId() - 110001] = true; - Database.CompleteQuest(playerSession.GetActor(), completed.Id); - Database.RemoveQuest(this, completed.Id); questScenario[slot] = null; playerWork.questScenario[slot] = 0; SendQuestClientUpdate(slot); // Reset active quest and quest state completed.OnComplete(); + Database.SaveCompletedQuests(playerSession.GetActor()); + Database.RemoveQuest(this, completed.Id); questStateManager.UpdateQuestCompleted(completed); // Msg Player @@ -1512,13 +1554,13 @@ namespace Meteor.Map.Actors } // Remove the quest from the DB and update client work values - Database.RemoveQuest(this, abandoned.Id); questScenario[slot] = null; playerWork.questScenario[slot] = 0; SendQuestClientUpdate(slot); // Reset active quest and quest state abandoned.OnAbandon(); + Database.RemoveQuest(this, abandoned.Id); questStateManager.UpdateQuestAbandoned(); // Msg Player diff --git a/Map Server/Actors/Quest/QuestStateManager.cs b/Map Server/Actors/Quest/QuestStateManager.cs index 23452b88..1bc0bebd 100644 --- a/Map Server/Actors/Quest/QuestStateManager.cs +++ b/Map Server/Actors/Quest/QuestStateManager.cs @@ -15,6 +15,7 @@ namespace Meteor.Map.Actors.QuestNS private const int SCENARIO_MAX = 2048; private readonly Player player; + private readonly Bitstream CompletedQuestsBitfield = new Bitstream(SCENARIO_MAX); private readonly Bitstream AvailableQuestsBitfield = new Bitstream(SCENARIO_MAX); private readonly Bitstream MinLevelBitfield = new Bitstream(SCENARIO_MAX); private readonly Bitstream PrereqBitfield = new Bitstream(SCENARIO_MAX, true); @@ -28,12 +29,12 @@ namespace Meteor.Map.Actors.QuestNS this.player = player; } - public void Init(Quest[] questScenario) + public void Init(Quest[] journalQuests, bool[] completedQuests) { // Preload any quests that the player loaded - if (questScenario != null) + if (journalQuests != null) { - foreach (var quest in questScenario) + foreach (var quest in journalQuests) { if (quest != null) { @@ -49,10 +50,10 @@ namespace Meteor.Map.Actors.QuestNS MinLevelBitfield.Set(questData.Id - SCENARIO_START); // Init Prereq - Bitstream completed = new Bitstream(player.playerWork.questScenarioComplete); + CompletedQuestsBitfield.SetTo(completedQuests); foreach (var questData in Server.GetQuestGamedataAllPrerequisite()) { - if (completed.Get(((Quest)Server.GetStaticActors(0xA0F00000 | questData.PrerequisiteQuest)).GetQuestId() - SCENARIO_START)) + if (CompletedQuestsBitfield.Get(((Quest)Server.GetStaticActors(0xA0F00000 | questData.PrerequisiteQuest)).GetQuestId() - SCENARIO_START)) PrereqBitfield.Set(questData.Id - SCENARIO_START); else PrereqBitfield.Clear(questData.Id - SCENARIO_START); @@ -71,6 +72,7 @@ namespace Meteor.Map.Actors.QuestNS public void UpdateQuestCompleted(Quest quest) { + CompletedQuestsBitfield.Set(quest.Id - SCENARIO_START); QuestGameData[] updated = Server.GetQuestGamedataByPrerequisite(quest.GetQuestId()); foreach (var questData in updated) PrereqBitfield.Set(questData.Id - SCENARIO_START); @@ -84,7 +86,8 @@ namespace Meteor.Map.Actors.QuestNS private void ComputeAvailable() { - Bitstream result = new Bitstream(player.playerWork.questScenarioComplete); + Bitstream result = new Bitstream(SCENARIO_MAX); + result.OR(CompletedQuestsBitfield); result.NOT(); result.AND(MinLevelBitfield); result.AND(PrereqBitfield); @@ -152,5 +155,10 @@ namespace Meteor.Map.Actors.QuestNS { return ActiveQuests.FindAll(quest => quest.IsQuestENPC(player, npc)).ToArray(); } + + public Bitstream GetCompletedBitstream() + { + return CompletedQuestsBitfield; + } } } diff --git a/Map Server/Database.cs b/Map Server/Database.cs index d7d47f1f..cd13735c 100644 --- a/Map Server/Database.cs +++ b/Map Server/Database.cs @@ -763,7 +763,7 @@ namespace Meteor.Map } } - public static void CompleteQuest(Player player, uint questId) + public static void SaveCompletedQuests(Player player) { string query; MySqlCommand cmd; @@ -776,15 +776,15 @@ namespace Meteor.Map query = @" INSERT INTO characters_quest_completed - (characterId, questId) + (characterId, completedQuests) VALUES - (@charaId, @questId) - ON DUPLICATE KEY UPDATE characterId=characterId + (@charaId, @completedQuests) + ON DUPLICATE KEY UPDATE completedQuests=completedQuests "; cmd = new MySqlCommand(query, conn); cmd.Parameters.AddWithValue("@charaId", player.Id); - cmd.Parameters.AddWithValue("@questId", 0xFFFFF & questId); + cmd.Parameters.AddWithValue("@completedQuests", Utils.ConvertBoolArrayToBinaryStream(player.playerWork.questScenarioComplete)); cmd.ExecuteNonQuery(); } @@ -799,31 +799,6 @@ namespace Meteor.Map } } - public static bool IsQuestCompleted(Player player, uint questId) - { - bool isCompleted = false; - 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(); - MySqlCommand cmd = new MySqlCommand("SELECT * FROM characters_quest_completed WHERE characterId = @charaId and questId = @questId", conn); - cmd.Parameters.AddWithValue("@charaId", player.Id); - cmd.Parameters.AddWithValue("@questId", questId); - isCompleted = cmd.ExecuteScalar() != null; - } - catch (MySqlException e) - { - Program.Log.Error(e.ToString()); - } - finally - { - conn.Dispose(); - } - } - return isCompleted; - } - public static void LoadPlayerCharacter(Player player) { string query; @@ -1267,6 +1242,25 @@ namespace Meteor.Map } } + //Load Completed Quests bitstream + query = @" + SELECT + completedQuests + FROM characters_quest_completed WHERE characterId = @charaId"; + + cmd = new MySqlCommand(query, conn); + cmd.Parameters.AddWithValue("@charaId", player.Id); + using (MySqlDataReader reader = cmd.ExecuteReader()) + { + // Replace the bool stream or use the default empty one. + if (reader.Read()) + { + byte[] bytes = new byte[256]; + reader.GetBytes(reader.GetOrdinal("completedQuests"), 0, bytes, 0, 256); + player.playerWork.questScenarioComplete = Utils.ConvertBinaryStreamToBoolArray(bytes); + } + } + //Load Local Guildleves query = @" SELECT diff --git a/Map Server/Map Server.csproj b/Map Server/Map Server.csproj index 3698fc77..4badd30b 100644 --- a/Map Server/Map Server.csproj +++ b/Map Server/Map Server.csproj @@ -227,7 +227,7 @@ - + diff --git a/Map Server/PacketProcessor.cs b/Map Server/PacketProcessor.cs index c36f761a..0c77c0af 100644 --- a/Map Server/PacketProcessor.cs +++ b/Map Server/PacketProcessor.cs @@ -39,6 +39,7 @@ using Meteor.Map.Actors; using Meteor.Map.packets.WorldPackets.Send; using Meteor.Map.packets.WorldPackets.Receive; using Meteor.Map.actors.director; +using System.Text; namespace Meteor.Map { @@ -49,345 +50,344 @@ namespace Meteor.Map public PacketProcessor(Server server) { mServer = server; - } + } public void ProcessPacket(ZoneConnection client, SubPacket subpacket) - { - Session session = mServer.GetSession(subpacket.header.sourceId); - - if (session == null && subpacket.gameMessage.opcode != 0x1000) - return; - - //Normal Game Opcode - switch (subpacket.gameMessage.opcode) - { - //World Server - Error - case 0x100A: - ErrorPacket worldError = new ErrorPacket(subpacket.data); - switch (worldError.errorCode) - { - case 0x01: - session.GetActor().SendGameMessage(Server.GetWorldManager().GetActor(), 60005, 0x20); - break; - } - break; - //World Server - Session Begin - case 0x1000: - subpacket.DebugPrintSubPacket(); - - SessionBeginPacket beginSessionPacket = new SessionBeginPacket(subpacket.data); - - session = mServer.AddSession(subpacket.header.sourceId); - - if (!beginSessionPacket.isLogin) - Server.GetWorldManager().DoZoneIn(session.GetActor(), false, session.GetActor().destinationSpawnType); - - Program.Log.Info("{0} has been added to the session list.", session.GetActor().DisplayName); - - client.FlushQueuedSendPackets(); - break; - //World Server - Session End - case 0x1001: - SessionEndPacket endSessionPacket = new SessionEndPacket(subpacket.data); - - if (endSessionPacket.destinationZoneId == 0) - session.GetActor().CleanupAndSave(); - else - session.GetActor().CleanupAndSave(endSessionPacket.destinationZoneId, endSessionPacket.destinationSpawnType, endSessionPacket.destinationX, endSessionPacket.destinationY, endSessionPacket.destinationZ, endSessionPacket.destinationRot); - - Server.GetServer().RemoveSession(session.id); - Program.Log.Info("{0} has been removed from the session list.", session.GetActor().DisplayName); - - session.QueuePacket(SessionEndConfirmPacket.BuildPacket(session, endSessionPacket.destinationZoneId)); - client.FlushQueuedSendPackets(); - break; - //World Server - Party Synch - case 0x1020: - PartySyncPacket partySyncPacket = new PartySyncPacket(subpacket.data); - Server.GetWorldManager().PartyMemberListRecieved(partySyncPacket); - break; - //World Server - Linkshell Creation Result - case 0x1025: - LinkshellResultPacket lsResult = new LinkshellResultPacket(subpacket.data); - LuaEngine.GetInstance().OnSignal("ls_result", lsResult.resultCode); - break; - //Ping - case 0x0001: - //subpacket.DebugPrintSubPacket(); - PingPacket pingPacket = new PingPacket(subpacket.data); - session.QueuePacket(PongPacket.BuildPacket(session.id, pingPacket.time)); - session.Ping(); - break; - //Unknown - case 0x0002: - - subpacket.DebugPrintSubPacket(); - session.QueuePacket(_0x2Packet.BuildPacket(session.id)); - client.FlushQueuedSendPackets(); - - break; - //Chat Received - case 0x0003: - ChatMessagePacket chatMessage = new ChatMessagePacket(subpacket.data); - //Program.Log.Info("Got type-{5} message: {0} @ {1}, {2}, {3}, Rot: {4}", chatMessage.message, chatMessage.posX, chatMessage.posY, chatMessage.posZ, chatMessage.posRot, chatMessage.logType); - - if (chatMessage.message.StartsWith("!")) - { - if (Server.GetCommandProcessor().DoCommand(chatMessage.message, session)) - return; ; - } - - if (chatMessage.logType == SendMessagePacket.MESSAGE_TYPE_SAY || chatMessage.logType == SendMessagePacket.MESSAGE_TYPE_SHOUT) - session.GetActor().BroadcastPacket(SendMessagePacket.BuildPacket(session.id, chatMessage.logType, session.GetActor().DisplayName, chatMessage.message), false); - - break; - //Langauge Code (Client safe to send packets to now) - case 0x0006: - LangaugeCodePacket langCode = new LangaugeCodePacket(subpacket.data); - LuaEngine.GetInstance().CallLuaFunction(session.GetActor(), session.GetActor(), "onBeginLogin", true); - Server.GetWorldManager().DoZoneIn(session.GetActor(), true, 0x1); - LuaEngine.GetInstance().CallLuaFunction(session.GetActor(), session.GetActor(), "onLogin", true); - session.languageCode = langCode.languageCode; - break; - //Unknown - Happens a lot at login, then once every time player zones - case 0x0007: - //subpacket.DebugPrintSubPacket(); - ZoneInCompletePacket zoneInCompletePacket = new ZoneInCompletePacket(subpacket.data); - break; - //Update Position - case 0x00CA: - //Update Position - UpdatePlayerPositionPacket posUpdate = new UpdatePlayerPositionPacket(subpacket.data); - session.UpdatePlayerActorPosition(posUpdate.x, posUpdate.y, posUpdate.z, posUpdate.rot, posUpdate.moveState); - session.GetActor().SendInstanceUpdate(); - - if (session.GetActor().IsInZoneChange()) - session.GetActor().SetZoneChanging(false); - - break; - //Set Target - case 0x00CD: - //subpacket.DebugPrintSubPacket(); - - SetTargetPacket setTarget = new SetTargetPacket(subpacket.data); - session.GetActor().currentTarget = setTarget.actorID; - session.GetActor().isAutoAttackEnabled = setTarget.attackTarget != 0xE0000000; - session.GetActor().BroadcastPacket(SetActorTargetAnimatedPacket.BuildPacket(session.id, setTarget.actorID), true); - break; - //Lock Target - case 0x00CC: - LockTargetPacket lockTarget = new LockTargetPacket(subpacket.data); - session.GetActor().currentLockedTarget = lockTarget.actorID; - break; - //Start Event - case 0x012D: - subpacket.DebugPrintSubPacket(); - EventStartPacket eventStart = new EventStartPacket(subpacket.data); - - /* - if (eventStart.error != null) - { - player.errorMessage += eventStart.error; - - if (eventStart.errorIndex == eventStart.errorNum - 1) - Program.Log.Error("\n"+player.errorMessage); + { + Session session = mServer.GetSession(subpacket.header.sourceId); + if (session == null && subpacket.gameMessage.opcode != 0x1000) + return; + //Normal Game Opcode + switch (subpacket.gameMessage.opcode) + { + //World Server - Error + case 0x100A: + ErrorPacket worldError = new ErrorPacket(subpacket.data); + switch (worldError.errorCode) + { + case 0x01: + session.GetActor().SendGameMessage(Server.GetWorldManager().GetActor(), 60005, 0x20); break; - } - */ + } + break; + //World Server - Session Begin + case 0x1000: + subpacket.DebugPrintSubPacket(); - Actor ownerActor = Server.GetStaticActors(eventStart.ownerActorID); - + SessionBeginPacket beginSessionPacket = new SessionBeginPacket(subpacket.data); + + session = mServer.AddSession(subpacket.header.sourceId); + + if (!beginSessionPacket.isLogin) + Server.GetWorldManager().DoZoneIn(session.GetActor(), false, session.GetActor().destinationSpawnType); + + Program.Log.Info("{0} has been added to the session list.", session.GetActor().DisplayName); + + client.FlushQueuedSendPackets(); + break; + //World Server - Session End + case 0x1001: + SessionEndPacket endSessionPacket = new SessionEndPacket(subpacket.data); + + if (endSessionPacket.destinationZoneId == 0) + session.GetActor().CleanupAndSave(); + else + session.GetActor().CleanupAndSave(endSessionPacket.destinationZoneId, endSessionPacket.destinationSpawnType, endSessionPacket.destinationX, endSessionPacket.destinationY, endSessionPacket.destinationZ, endSessionPacket.destinationRot); + + Server.GetServer().RemoveSession(session.id); + Program.Log.Info("{0} has been removed from the session list.", session.GetActor().DisplayName); + + session.QueuePacket(SessionEndConfirmPacket.BuildPacket(session, endSessionPacket.destinationZoneId)); + client.FlushQueuedSendPackets(); + break; + //World Server - Party Synch + case 0x1020: + PartySyncPacket partySyncPacket = new PartySyncPacket(subpacket.data); + Server.GetWorldManager().PartyMemberListRecieved(partySyncPacket); + break; + //World Server - Linkshell Creation Result + case 0x1025: + LinkshellResultPacket lsResult = new LinkshellResultPacket(subpacket.data); + LuaEngine.GetInstance().OnSignal("ls_result", lsResult.resultCode); + break; + //Ping + case 0x0001: + //subpacket.DebugPrintSubPacket(); + PingPacket pingPacket = new PingPacket(subpacket.data); + session.QueuePacket(PongPacket.BuildPacket(session.id, pingPacket.time)); + session.Ping(); + break; + //Unknown + case 0x0002: + + subpacket.DebugPrintSubPacket(); + session.QueuePacket(_0x2Packet.BuildPacket(session.id)); + client.FlushQueuedSendPackets(); + + break; + //Chat Received + case 0x0003: + ChatMessagePacket chatMessage = new ChatMessagePacket(subpacket.data); + //Program.Log.Info("Got type-{5} message: {0} @ {1}, {2}, {3}, Rot: {4}", chatMessage.message, chatMessage.posX, chatMessage.posY, chatMessage.posZ, chatMessage.posRot, chatMessage.logType); + + if (chatMessage.message.StartsWith("!")) + { + if (Server.GetCommandProcessor().DoCommand(chatMessage.message, session)) + return; ; + } + + if (chatMessage.logType == SendMessagePacket.MESSAGE_TYPE_SAY || chatMessage.logType == SendMessagePacket.MESSAGE_TYPE_SHOUT) + session.GetActor().BroadcastPacket(SendMessagePacket.BuildPacket(session.id, chatMessage.logType, session.GetActor().DisplayName, chatMessage.message), false); + + break; + //Langauge Code (Client safe to send packets to now) + case 0x0006: + LangaugeCodePacket langCode = new LangaugeCodePacket(subpacket.data); + LuaEngine.GetInstance().CallLuaFunction(session.GetActor(), session.GetActor(), "onBeginLogin", true); + Server.GetWorldManager().DoZoneIn(session.GetActor(), true, 0x1); + LuaEngine.GetInstance().CallLuaFunction(session.GetActor(), session.GetActor(), "onLogin", true); + session.languageCode = langCode.languageCode; + break; + //Unknown - Happens a lot at login, then once every time player zones + case 0x0007: + //subpacket.DebugPrintSubPacket(); + ZoneInCompletePacket zoneInCompletePacket = new ZoneInCompletePacket(subpacket.data); + break; + //Update Position + case 0x00CA: + //Update Position + UpdatePlayerPositionPacket posUpdate = new UpdatePlayerPositionPacket(subpacket.data); + session.UpdatePlayerActorPosition(posUpdate.x, posUpdate.y, posUpdate.z, posUpdate.rot, posUpdate.moveState); + session.GetActor().SendInstanceUpdate(); + + if (session.GetActor().IsInZoneChange()) + session.GetActor().SetZoneChanging(false); + + break; + //Set Target + case 0x00CD: + //subpacket.DebugPrintSubPacket(); + + SetTargetPacket setTarget = new SetTargetPacket(subpacket.data); + session.GetActor().currentTarget = setTarget.actorID; + session.GetActor().isAutoAttackEnabled = setTarget.attackTarget != 0xE0000000; + session.GetActor().BroadcastPacket(SetActorTargetAnimatedPacket.BuildPacket(session.id, setTarget.actorID), true); + break; + //Lock Target + case 0x00CC: + LockTargetPacket lockTarget = new LockTargetPacket(subpacket.data); + session.GetActor().currentLockedTarget = lockTarget.actorID; + break; + //Start Event + case 0x012D: + subpacket.DebugPrintSubPacket(); + EventStartPacket eventStart = new EventStartPacket(subpacket.data); + + /* + if (eventStart.error != null) + { + player.errorMessage += eventStart.error; + + if (eventStart.errorIndex == eventStart.errorNum - 1) + Program.Log.Error("\n"+player.errorMessage); + + + break; + } + */ + + Actor ownerActor = Server.GetStaticActors(eventStart.ownerActorID); + + if (ownerActor == null) + { + //Is it your retainer? + if (session.GetActor().currentSpawnedRetainer != null && session.GetActor().currentSpawnedRetainer.Id == eventStart.ownerActorID) + ownerActor = session.GetActor().currentSpawnedRetainer; + //Is it a instance actor? + if (ownerActor == null) + ownerActor = session.GetActor().CurrentArea.FindActorInArea(eventStart.ownerActorID); + //Is it a Director? if (ownerActor == null) { - //Is it your retainer? - if (session.GetActor().currentSpawnedRetainer != null && session.GetActor().currentSpawnedRetainer.Id == eventStart.ownerActorID) - ownerActor = session.GetActor().currentSpawnedRetainer; - //Is it a instance actor? - if (ownerActor == null) - ownerActor = session.GetActor().CurrentArea.FindActorInArea(eventStart.ownerActorID); - //Is it a Director? - if (ownerActor == null) + Director director = session.GetActor().GetDirector(eventStart.ownerActorID); + if (director != null) + ownerActor = director; + else { - Director director = session.GetActor().GetDirector(eventStart.ownerActorID); - if (director != null) - ownerActor = director; - else - { - Program.Log.Debug("\n===Event START===\nCould not find actor 0x{0:X} for event started by caller: 0x{1:X}\nEvent Starter: {2}\nParams: {3}", eventStart.triggerActorID, eventStart.ownerActorID, eventStart.eventName, LuaUtils.DumpParams(eventStart.luaParams)); - break; - } - } - } - - session.GetActor().StartEvent(ownerActor, eventStart); - - Program.Log.Debug("\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.triggerActorID, eventStart.ownerActorID, eventStart.serverCodes, eventStart.unknown, eventStart.eventName, LuaUtils.DumpParams(eventStart.luaParams)); - break; - //Unknown, happens at npc spawn and cutscene play???? - case 0x00CE: - subpacket.DebugPrintSubPacket(); - break; - //Countdown requested - case 0x00CF: - CountdownRequestPacket countdownPacket = new CountdownRequestPacket(subpacket.data); - session.GetActor().BroadcastCountdown(countdownPacket.countdownLength, countdownPacket.syncTime); - break; - //Event Result - case 0x012E: - subpacket.DebugPrintSubPacket(); - EventUpdatePacket eventUpdate = new EventUpdatePacket(subpacket.data); - Program.Log.Debug("\n===Event UPDATE===\nSource Actor: 0x{0:X}\nCaller Actor: 0x{1:X}\nVal1: 0x{2:X}\nVal2: 0x{3:X}\nStep: 0x{4:X}\nParams: {5}", eventUpdate.triggerActorID, eventUpdate.serverCodes, eventUpdate.unknown1, eventUpdate.unknown2, eventUpdate.eventType, LuaUtils.DumpParams(eventUpdate.luaParams)); - /* - //Is it a static actor? If not look in the player's instance - Actor updateOwnerActor = Server.GetStaticActors(session.GetActor().currentEventOwner); - if (updateOwnerActor == null) - { - updateOwnerActor = Server.GetWorldManager().GetActorInWorld(session.GetActor().currentEventOwner); - - if (session.GetActor().currentDirector != null && session.GetActor().currentEventOwner == session.GetActor().currentDirector.actorId) - updateOwnerActor = session.GetActor().currentDirector; - - if (updateOwnerActor == null) + Program.Log.Debug("\n===Event START===\nCould not find actor 0x{0:X} for event started by caller: 0x{1:X}\nEvent Starter: {2}\nParams: {3}", eventStart.triggerActorID, eventStart.ownerActorID, eventStart.eventName, LuaUtils.DumpParams(eventStart.luaParams)); break; + } } - */ - session.GetActor().UpdateEvent(eventUpdate); + } - //LuaEngine.DoActorOnEventUpdated(session.GetActor(), updateOwnerActor, eventUpdate); - - break; - case 0x012F: - subpacket.DebugPrintSubPacket(); - ParameterDataRequestPacket paramRequest = new ParameterDataRequestPacket(subpacket.data); - if (paramRequest.paramName.Equals("charaWork/exp")) - session.GetActor().SendCharaExpInfo(); - break; - //Item Package Request - case 0x0131: - UpdateItemPackagePacket packageRequest = new UpdateItemPackagePacket(subpacket.data); - if (Server.GetWorldManager().GetActorInWorld(packageRequest.actorID) != null) - { - ((Character)Server.GetWorldManager().GetActorInWorld(packageRequest.actorID)).SendItemPackage(session.GetActor(), packageRequest.packageId); + session.GetActor().StartEvent(ownerActor, eventStart); + + Program.Log.Debug("\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.triggerActorID, eventStart.ownerActorID, eventStart.serverCodes, eventStart.unknown, eventStart.eventName, LuaUtils.DumpParams(eventStart.luaParams)); + break; + //Unknown, happens at npc spawn and cutscene play???? + case 0x00CE: + subpacket.DebugPrintSubPacket(); + break; + //Countdown requested + case 0x00CF: + CountdownRequestPacket countdownPacket = new CountdownRequestPacket(subpacket.data); + session.GetActor().BroadcastCountdown(countdownPacket.countdownLength, countdownPacket.syncTime); + break; + //Event Result + case 0x012E: + subpacket.DebugPrintSubPacket(); + EventUpdatePacket eventUpdate = new EventUpdatePacket(subpacket.data); + Program.Log.Debug("\n===Event UPDATE===\nSource Actor: 0x{0:X}\nCaller Actor: 0x{1:X}\nVal1: 0x{2:X}\nVal2: 0x{3:X}\nStep: 0x{4:X}\nParams: {5}", eventUpdate.triggerActorID, eventUpdate.serverCodes, eventUpdate.unknown1, eventUpdate.unknown2, eventUpdate.eventType, LuaUtils.DumpParams(eventUpdate.luaParams)); + /* + //Is it a static actor? If not look in the player's instance + Actor updateOwnerActor = Server.GetStaticActors(session.GetActor().currentEventOwner); + if (updateOwnerActor == null) + { + updateOwnerActor = Server.GetWorldManager().GetActorInWorld(session.GetActor().currentEventOwner); + + if (session.GetActor().currentDirector != null && session.GetActor().currentEventOwner == session.GetActor().currentDirector.actorId) + updateOwnerActor = session.GetActor().currentDirector; + + if (updateOwnerActor == null) break; - } - if (session.GetActor().GetSpawnedRetainer() != null && session.GetActor().GetSpawnedRetainer().Id == packageRequest.actorID) - session.GetActor().GetSpawnedRetainer().SendItemPackage(session.GetActor(), packageRequest.packageId); + } + */ + session.GetActor().UpdateEvent(eventUpdate); + + //LuaEngine.DoActorOnEventUpdated(session.GetActor(), updateOwnerActor, eventUpdate); + + break; + case 0x012F: + subpacket.DebugPrintSubPacket(); + WorkSyncRequestPacket workSyncRequest = new WorkSyncRequestPacket(subpacket.data); + session.GetActor().OnWorkSyncRequest(workSyncRequest.propertyName, workSyncRequest.from, workSyncRequest.to); + break; + //Item Package Request + case 0x0131: + UpdateItemPackagePacket packageRequest = new UpdateItemPackagePacket(subpacket.data); + if (Server.GetWorldManager().GetActorInWorld(packageRequest.actorID) != null) + { + ((Character)Server.GetWorldManager().GetActorInWorld(packageRequest.actorID)).SendItemPackage(session.GetActor(), packageRequest.packageId); break; - //Group Created Confirm - case 0x0133: - GroupCreatedPacket groupCreated = new GroupCreatedPacket(subpacket.data); - Server.GetWorldManager().SendGroupInit(session, groupCreated.groupId); - break; - //Achievement Progress Request - case 0x0135: - AchievementProgressRequestPacket progressRequest = new AchievementProgressRequestPacket(subpacket.data); - session.QueuePacket(Database.GetAchievementProgress(session.GetActor(), progressRequest.achievementId)); - break; - /* RECRUITMENT */ - //Start Recruiting - case 0x01C3: - StartRecruitingRequestPacket recruitRequestPacket = new StartRecruitingRequestPacket(subpacket.data); - session.QueuePacket(StartRecruitingResponse.BuildPacket(session.id, true)); - break; - //End Recruiting - case 0x01C4: - session.QueuePacket(EndRecruitmentPacket.BuildPacket(session.id)); - break; - //Party Window Opened, Request State - case 0x01C5: - session.QueuePacket(RecruiterStatePacket.BuildPacket(session.id, false, false, 0)); - break; - //Search Recruiting - case 0x01C7: - RecruitmentSearchRequestPacket recruitSearchPacket = new RecruitmentSearchRequestPacket(subpacket.data); - break; - //Get Recruitment Details - case 0x01C8: - RecruitmentDetailsRequestPacket currentRecruitDetailsPacket = new RecruitmentDetailsRequestPacket(subpacket.data); - RecruitmentDetails details = new RecruitmentDetails(); - details.recruiterName = "Localhost Character"; - details.purposeId = 2; - details.locationId = 1; - details.subTaskId = 1; - details.comment = "This is a test details packet sent by the server. No implementation has been Created yet..."; - details.num[0] = 1; - session.QueuePacket(CurrentRecruitmentDetailsPacket.BuildPacket(session.id, details)); - break; - //Accepted Recruiting - case 0x01C6: - subpacket.DebugPrintSubPacket(); - break; - /* SOCIAL STUFF */ - case 0x01C9: - AddRemoveSocialPacket addBlackList = new AddRemoveSocialPacket(subpacket.data); - session.QueuePacket(BlacklistAddedPacket.BuildPacket(session.id, true, addBlackList.name)); - break; - case 0x01CA: - AddRemoveSocialPacket RemoveBlackList = new AddRemoveSocialPacket(subpacket.data); - session.QueuePacket(BlacklistRemovedPacket.BuildPacket(session.id, true, RemoveBlackList.name)); - break; - case 0x01CB: - int offset1 = 0; - session.QueuePacket(SendBlacklistPacket.BuildPacket(session.id, new String[] { "Test" }, ref offset1)); - break; - case 0x01CC: - AddRemoveSocialPacket addFriendList = new AddRemoveSocialPacket(subpacket.data); - session.QueuePacket(FriendlistAddedPacket.BuildPacket(session.id, true, (uint)addFriendList.name.GetHashCode(), true, addFriendList.name)); - break; - case 0x01CD: - AddRemoveSocialPacket RemoveFriendList = new AddRemoveSocialPacket(subpacket.data); - session.QueuePacket(FriendlistRemovedPacket.BuildPacket(session.id, true, RemoveFriendList.name)); - break; - case 0x01CE: - int offset2 = 0; - session.QueuePacket(SendFriendlistPacket.BuildPacket(session.id, new Tuple[] { new Tuple(01, "Test2") }, ref offset2)); - break; - case 0x01CF: - session.QueuePacket(FriendStatusPacket.BuildPacket(session.id, null)); - break; - /* SUPPORT DESK STUFF */ - //Request for FAQ/Info List - case 0x01D0: - FaqListRequestPacket faqRequest = new FaqListRequestPacket(subpacket.data); - session.QueuePacket(FaqListResponsePacket.BuildPacket(session.id, new string[] { "Testing FAQ1", "Coded style!" })); - break; - //Request for body of a faq/info selection - case 0x01D1: - FaqBodyRequestPacket faqBodyRequest = new FaqBodyRequestPacket(subpacket.data); - session.QueuePacket(FaqBodyResponsePacket.BuildPacket(session.id, "HERE IS A GIANT BODY. Nothing else to say!")); - break; - //Request issue list - case 0x01D2: - GMTicketIssuesRequestPacket issuesRequest = new GMTicketIssuesRequestPacket(subpacket.data); - session.QueuePacket(IssueListResponsePacket.BuildPacket(session.id, new string[] { "Test1", "Test2", "Test3", "Test4", "Test5" })); - break; - //Request if GM ticket exists - case 0x01D3: - session.QueuePacket(StartGMTicketPacket.BuildPacket(session.id, false)); - break; - //Request for GM response message - case 0x01D4: - session.QueuePacket(GMTicketPacket.BuildPacket(session.id, "This is a GM Ticket Title", "This is a GM Ticket Body.")); - break; - //GM Ticket Sent - case 0x01D5: - GMSupportTicketPacket gmTicket = new GMSupportTicketPacket(subpacket.data); - Program.Log.Info("Got GM Ticket: \n" + gmTicket.ticketTitle + "\n" + gmTicket.ticketBody); - session.QueuePacket(GMTicketSentResponsePacket.BuildPacket(session.id, true)); - break; - //Request to end ticket - case 0x01D6: - session.QueuePacket(EndGMTicketPacket.BuildPacket(session.id)); - break; - default: - Program.Log.Debug("Unknown command 0x{0:X} received.", subpacket.gameMessage.opcode); - subpacket.DebugPrintSubPacket(); - break; - } - - } + } + if (session.GetActor().GetSpawnedRetainer() != null && session.GetActor().GetSpawnedRetainer().Id == packageRequest.actorID) + session.GetActor().GetSpawnedRetainer().SendItemPackage(session.GetActor(), packageRequest.packageId); + break; + //Group Created Confirm + case 0x0133: + GroupCreatedPacket groupCreated = new GroupCreatedPacket(subpacket.data); + Server.GetWorldManager().SendGroupInit(session, groupCreated.groupId); + break; + //Achievement Progress Request + case 0x0135: + AchievementProgressRequestPacket progressRequest = new AchievementProgressRequestPacket(subpacket.data); + session.QueuePacket(Database.GetAchievementProgress(session.GetActor(), progressRequest.achievementId)); + break; + /* RECRUITMENT */ + //Start Recruiting + case 0x01C3: + StartRecruitingRequestPacket recruitRequestPacket = new StartRecruitingRequestPacket(subpacket.data); + session.QueuePacket(StartRecruitingResponse.BuildPacket(session.id, true)); + break; + //End Recruiting + case 0x01C4: + session.QueuePacket(EndRecruitmentPacket.BuildPacket(session.id)); + break; + //Party Window Opened, Request State + case 0x01C5: + session.QueuePacket(RecruiterStatePacket.BuildPacket(session.id, false, false, 0)); + break; + //Search Recruiting + case 0x01C7: + RecruitmentSearchRequestPacket recruitSearchPacket = new RecruitmentSearchRequestPacket(subpacket.data); + break; + //Get Recruitment Details + case 0x01C8: + RecruitmentDetailsRequestPacket currentRecruitDetailsPacket = new RecruitmentDetailsRequestPacket(subpacket.data); + RecruitmentDetails details = new RecruitmentDetails(); + details.recruiterName = "Localhost Character"; + details.purposeId = 2; + details.locationId = 1; + details.subTaskId = 1; + details.comment = "This is a test details packet sent by the server. No implementation has been Created yet..."; + details.num[0] = 1; + session.QueuePacket(CurrentRecruitmentDetailsPacket.BuildPacket(session.id, details)); + break; + //Accepted Recruiting + case 0x01C6: + subpacket.DebugPrintSubPacket(); + break; + /* SOCIAL STUFF */ + case 0x01C9: + AddRemoveSocialPacket addBlackList = new AddRemoveSocialPacket(subpacket.data); + session.QueuePacket(BlacklistAddedPacket.BuildPacket(session.id, true, addBlackList.name)); + break; + case 0x01CA: + AddRemoveSocialPacket RemoveBlackList = new AddRemoveSocialPacket(subpacket.data); + session.QueuePacket(BlacklistRemovedPacket.BuildPacket(session.id, true, RemoveBlackList.name)); + break; + case 0x01CB: + int offset1 = 0; + session.QueuePacket(SendBlacklistPacket.BuildPacket(session.id, new String[] { "Test" }, ref offset1)); + break; + case 0x01CC: + AddRemoveSocialPacket addFriendList = new AddRemoveSocialPacket(subpacket.data); + session.QueuePacket(FriendlistAddedPacket.BuildPacket(session.id, true, (uint)addFriendList.name.GetHashCode(), true, addFriendList.name)); + break; + case 0x01CD: + AddRemoveSocialPacket RemoveFriendList = new AddRemoveSocialPacket(subpacket.data); + session.QueuePacket(FriendlistRemovedPacket.BuildPacket(session.id, true, RemoveFriendList.name)); + break; + case 0x01CE: + int offset2 = 0; + session.QueuePacket(SendFriendlistPacket.BuildPacket(session.id, new Tuple[] { new Tuple(01, "Test2") }, ref offset2)); + break; + case 0x01CF: + session.QueuePacket(FriendStatusPacket.BuildPacket(session.id, null)); + break; + /* SUPPORT DESK STUFF */ + //Request for FAQ/Info List + case 0x01D0: + FaqListRequestPacket faqRequest = new FaqListRequestPacket(subpacket.data); + session.QueuePacket(FaqListResponsePacket.BuildPacket(session.id, new string[] { "Testing FAQ1", "Coded style!" })); + break; + //Request for body of a faq/info selection + case 0x01D1: + FaqBodyRequestPacket faqBodyRequest = new FaqBodyRequestPacket(subpacket.data); + session.QueuePacket(FaqBodyResponsePacket.BuildPacket(session.id, "HERE IS A GIANT BODY. Nothing else to say!")); + break; + //Request issue list + case 0x01D2: + GMTicketIssuesRequestPacket issuesRequest = new GMTicketIssuesRequestPacket(subpacket.data); + session.QueuePacket(IssueListResponsePacket.BuildPacket(session.id, new string[] { "Test1", "Test2", "Test3", "Test4", "Test5" })); + break; + //Request if GM ticket exists + case 0x01D3: + session.QueuePacket(StartGMTicketPacket.BuildPacket(session.id, false)); + break; + //Request for GM response message + case 0x01D4: + session.QueuePacket(GMTicketPacket.BuildPacket(session.id, "This is a GM Ticket Title", "This is a GM Ticket Body.")); + break; + //GM Ticket Sent + case 0x01D5: + GMSupportTicketPacket gmTicket = new GMSupportTicketPacket(subpacket.data); + Program.Log.Info("Got GM Ticket: \n" + gmTicket.ticketTitle + "\n" + gmTicket.ticketBody); + session.QueuePacket(GMTicketSentResponsePacket.BuildPacket(session.id, true)); + break; + //Request to end ticket + case 0x01D6: + session.QueuePacket(EndGMTicketPacket.BuildPacket(session.id)); + break; + default: + Program.Log.Debug("Unknown command 0x{0:X} received.", subpacket.gameMessage.opcode); + subpacket.DebugPrintSubPacket(); + break; + } + + } } } diff --git a/Map Server/Packets/Receive/ParameterDataRequestPacket.cs b/Map Server/Packets/Receive/WorkSyncRequestPacket.cs similarity index 65% rename from Map Server/Packets/Receive/ParameterDataRequestPacket.cs rename to Map Server/Packets/Receive/WorkSyncRequestPacket.cs index e67308ed..08d57532 100644 --- a/Map Server/Packets/Receive/ParameterDataRequestPacket.cs +++ b/Map Server/Packets/Receive/WorkSyncRequestPacket.cs @@ -26,7 +26,7 @@ using System.Text; namespace Meteor.Map.packets.receive { - class ParameterDataRequestPacket + class WorkSyncRequestPacket { public const ushort OPCODE = 0x012F; public const uint PACKET_SIZE = 0x48; @@ -34,9 +34,11 @@ namespace Meteor.Map.packets.receive public bool invalidPacket = false; public uint actorID; - public string paramName; + public string propertyName; + public ushort from, to; + public bool requestingBitfield = false; - public ParameterDataRequestPacket(byte[] data) + public WorkSyncRequestPacket(byte[] data) { using (MemoryStream mem = new MemoryStream(data)) { @@ -44,13 +46,22 @@ namespace Meteor.Map.packets.receive { try{ actorID = binReader.ReadUInt32(); - List strList = new List(); - byte curByte; - while ((curByte = binReader.ReadByte()) != 0 && strList.Count<=0x20) + if (binReader.PeekChar() == 9) { - strList.Add(curByte); + binReader.ReadByte(); + from = binReader.ReadUInt16(); + to = binReader.ReadUInt16(); } - paramName = Encoding.ASCII.GetString(strList.ToArray()); + + byte currentByte; + int size = 0; + long strPos = binReader.BaseStream.Position; + while ((currentByte = binReader.ReadByte()) != 0 && size <= 0x20) + size++; + + binReader.BaseStream.Seek(strPos, SeekOrigin.Begin); + byte[] str = binReader.ReadBytes(size); + propertyName = Encoding.ASCII.GetString(str); } catch (Exception){ invalidPacket = true; diff --git a/Map Server/Packets/Send/Actor/SetActorPropetyPacket.cs b/Map Server/Packets/Send/Actor/SetActorPropetyPacket.cs index 64fa5513..f5d1d969 100644 --- a/Map Server/Packets/Send/Actor/SetActorPropetyPacket.cs +++ b/Map Server/Packets/Send/Actor/SetActorPropetyPacket.cs @@ -44,6 +44,10 @@ namespace Meteor.Map.packets.send.actor string currentTarget; + bool isBitfield = false; + ushort from; + ushort to; + private MemoryStream mem; private BinaryWriter binWriter; @@ -54,6 +58,16 @@ namespace Meteor.Map.packets.send.actor binWriter.Seek(1, SeekOrigin.Begin); currentTarget = startingTarget; } + public SetActorPropetyPacket(ushort from, ushort to, string startingTarget) + { + mem = new MemoryStream(data); + binWriter = new BinaryWriter(mem); + binWriter.Seek(1, SeekOrigin.Begin); + currentTarget = startingTarget; + this.from = from; + this.to = to; + isBitfield = true; + } public void CloseStreams() { @@ -100,6 +114,19 @@ namespace Meteor.Map.packets.send.actor return true; } + public bool AddBitfield(uint id, byte[] data) + { + if (runningByteTotal + 5 + data.Length + 1 + (1 + 5 + Encoding.ASCII.GetByteCount(currentTarget)) > MAXBYTES) + return false; + + binWriter.Write((byte) (data.Length)); + binWriter.Write((UInt32)id); + binWriter.Write(data); + runningByteTotal += (ushort)(5 + data.Length); + + return true; + } + public bool AddBuffer(uint id, byte[] buffer) { if (runningByteTotal + 5 + buffer.Length + (1 + Encoding.ASCII.GetByteCount(currentTarget)) > MAXBYTES) @@ -208,10 +235,22 @@ namespace Meteor.Map.packets.send.actor public void SetTarget(string target) { currentTarget = target; + isBitfield = false; } public void AddTarget() { + if (isBitfield) + { + binWriter.Write((byte)(isMore ? 0x60 + currentTarget.Length + 5 : 0x82 + currentTarget.Length + 5)); + binWriter.Write((byte)9); + binWriter.Write(from); + binWriter.Write(to); + binWriter.Write(Encoding.ASCII.GetBytes(currentTarget)); + runningByteTotal += (ushort)(6 + Encoding.ASCII.GetByteCount(currentTarget)); + return; + } + if (isArrayMode) binWriter.Write((byte)(0xA4 + currentTarget.Length)); else @@ -236,6 +275,7 @@ namespace Meteor.Map.packets.send.actor CloseStreams(); SubPacket packet = new SubPacket(OPCODE, sourceActorId, data); + packet.DebugPrintSubPacket(); return packet; } diff --git a/Map Server/Program.cs b/Map Server/Program.cs index 23c524b9..5e7f28d7 100644 --- a/Map Server/Program.cs +++ b/Map Server/Program.cs @@ -21,6 +21,7 @@ along with Project Meteor Server. If not, see . using System; using System.Diagnostics; +using Meteor.Common; using MySql.Data.MySqlClient; using NLog; @@ -35,7 +36,7 @@ namespace Meteor.Map public static DateTime Tick = DateTime.Now; static void Main(string[] args) - { + { // set up logging Log = LogManager.GetCurrentClassLogger(); #if DEBUG