diff --git a/FFXIVClassic Map Server/FFXIVClassic Map Server.csproj b/FFXIVClassic Map Server/FFXIVClassic Map Server.csproj index dcd48884..8a1c780b 100644 --- a/FFXIVClassic Map Server/FFXIVClassic Map Server.csproj +++ b/FFXIVClassic Map Server/FFXIVClassic Map Server.csproj @@ -76,6 +76,7 @@ + @@ -88,11 +89,12 @@ + + - @@ -209,9 +211,13 @@ + + + + diff --git a/FFXIVClassic Map Server/PacketProcessor.cs b/FFXIVClassic Map Server/PacketProcessor.cs index 5a175e6d..16d29482 100644 --- a/FFXIVClassic Map Server/PacketProcessor.cs +++ b/FFXIVClassic Map Server/PacketProcessor.cs @@ -182,7 +182,7 @@ namespace FFXIVClassic_Map_Server if (ownerActor == null) { //Is it a instance actor? - ownerActor = session.GetActor().zone.FindActorInZone(session.GetActor().currentEventOwner); + ownerActor = session.GetActor().zone.FindActorInArea(session.GetActor().currentEventOwner); if (ownerActor == null) { //Is it a Director? @@ -238,7 +238,7 @@ namespace FFXIVClassic_Map_Server //Group Created Confirm case 0x0133: GroupCreatedPacket groupCreated = new GroupCreatedPacket(subpacket.data); - + Server.GetWorldManager().SendGroupInit(session, groupCreated.groupId); break; /* RECRUITMENT */ //Start Recruiting diff --git a/FFXIVClassic Map Server/WorldManager.cs b/FFXIVClassic Map Server/WorldManager.cs index 253413cb..e619888b 100644 --- a/FFXIVClassic Map Server/WorldManager.cs +++ b/FFXIVClassic Map Server/WorldManager.cs @@ -36,13 +36,16 @@ namespace FFXIVClassic_Map_Server private Dictionary actorClasses = new Dictionary(); private Dictionary currentPlayerParties = new Dictionary(); //GroupId, Party object - private Object groupLock = new Object(); - private Server mServer; private const int MILIS_LOOPTIME = 10; private Timer mZoneTimer; + //Content Groups + public Dictionary mContentGroups = new Dictionary(); + private Object groupLock = new Object(); + public ulong groupIndexId = 1; + public WorldManager(Server server) { mServer = server; @@ -693,6 +696,68 @@ namespace FFXIVClassic_Map_Server //LoadNPCs(zone.actorId); } + + public ContentGroup CreateContentGroup(Director director) + { + return CreateContentGroup(director, null); + } + + public ContentGroup CreateContentGroup(Director director, params Actor[] actors) + { + if (director == null) + return null; + + lock (groupLock) + { + uint[] initialMembers = null; + + if (actors != null) + { + initialMembers = new uint[actors.Length]; + for (int i = 0; i < actors.Length; i++) + initialMembers[i] = actors[i].actorId; + } + + groupIndexId = groupIndexId | 0x3000000000000000; + + ContentGroup contentGroup = new ContentGroup(groupIndexId, director, initialMembers); + mContentGroups.Add(groupIndexId, contentGroup); + groupIndexId++; + if (initialMembers != null && initialMembers.Length != 0) + contentGroup.SendAll(); + + return contentGroup; + } + } + + public void DeleteContentGroup(ulong groupId) + { + lock (groupLock) + { + if (mContentGroups.ContainsKey(groupId) && mContentGroups[groupId] is ContentGroup) + { + ContentGroup group = (ContentGroup)mContentGroups[groupId]; + group.SendDeletePackets(); + mContentGroups.Remove(groupId); + } + } + } + + public void CreateContentArea(String scriptPath) + { + LuaScript script = LuaEngine.LoadScript(scriptPath); + + } + + public bool SendGroupInit(Session session, ulong groupId) + { + if (mContentGroups.ContainsKey(groupId)) + { + mContentGroups[groupId].SendInitWorkValues(session); + return true; + } + return false; + } public void RequestWorldLinkshellCreate(Player player, string name, ushort crest) { @@ -857,7 +922,7 @@ namespace FFXIVClassic_Map_Server { Actor a = zone.FindActorInZone(charId); if (a != null) - return a; + return a; } return null; } diff --git a/FFXIVClassic Map Server/actors/area/Area.cs b/FFXIVClassic Map Server/actors/area/Area.cs index 94c72444..1f060e63 100644 --- a/FFXIVClassic Map Server/actors/area/Area.cs +++ b/FFXIVClassic Map Server/actors/area/Area.cs @@ -1,376 +1,371 @@ -using FFXIVClassic_Map_Server; -using FFXIVClassic.Common; - -using FFXIVClassic_Map_Server.actors.area; -using FFXIVClassic_Map_Server.actors.chara.npc; -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; -using System.Text; -using System.Threading.Tasks; +using FFXIVClassic_Map_Server; +using FFXIVClassic.Common; + +using FFXIVClassic_Map_Server.actors.area; +using FFXIVClassic_Map_Server.actors.chara.npc; +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; +using System.Text; +using System.Threading.Tasks; using FFXIVClassic_Map_Server.packets.send; using FFXIVClassic_Map_Server.actors.group; using FFXIVClassic_Map_Server.actors.director; -namespace FFXIVClassic_Map_Server.Actors -{ - class Area : Actor - { - public string zoneName; - public ushort regionId; - public bool isIsolated, canStealth, isInn, canRideChocobo, isInstanceRaid; - public ushort weatherNormal, weatherCommon, weatherRare; - public ushort bgmDay, bgmNight, bgmBattle; - - protected string classPath; - - public int boundingGridSize = 50; - public int minX = -1000, minY = -1000, maxX = 1000, maxY = 1000; - protected int numXBlocks, numYBlocks; +namespace FFXIVClassic_Map_Server.Actors +{ + class Area : Actor + { + public string zoneName; + public ushort regionId; + public bool isIsolated, canStealth, isInn, canRideChocobo, isInstanceRaid; + public ushort weatherNormal, weatherCommon, weatherRare; + public ushort bgmDay, bgmNight, bgmBattle; + + protected string classPath; + + public int boundingGridSize = 50; + public int minX = -1000, minY = -1000, maxX = 1000, maxY = 1000; + protected int numXBlocks, numYBlocks; protected int halfWidth, halfHeight; private Dictionary currentDirectors = new Dictionary(); private Object directorLock = new Object(); private uint directorIdCount = 0; - protected Director mWeatherDirector; - - protected List mSpawnLocations = new List(); - protected Dictionary mActorList = new Dictionary(); - protected List[,] mActorBlock; - - LuaScript areaScript; + protected Director mWeatherDirector; - //Content Groups - public Dictionary mContentGroups = new Dictionary(); - private Object groupLock = new Object(); - public ulong groupIndexId = 0; - - 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) - { - - this.zoneName = zoneName; - this.regionId = regionId; - this.canStealth = canStealth; - this.isIsolated = isIsolated; - this.isInn = isInn; - this.canRideChocobo = canRideChocobo; - this.isInstanceRaid = isInstanceRaid; - - this.bgmDay = bgmDay; - this.bgmNight = bgmNight; - this.bgmBattle = bgmBattle; - - this.displayNameId = 0; - this.customDisplayName = "_areaMaster"; - this.actorName = String.Format("_areaMaster@{0:X5}",id<<8); - - this.className = className; - - numXBlocks = (maxX - minX) / boundingGridSize; - numYBlocks = (maxY - minY) / boundingGridSize; - mActorBlock = new List[numXBlocks, numYBlocks]; - halfWidth = numXBlocks / 2; - halfHeight = numYBlocks / 2; - - for (int y = 0; y < numYBlocks; y++) - { - for (int x = 0; x < numXBlocks; x++ ) - { - mActorBlock[x, y] = new List(); - } - } - - } - - public override SubPacket CreateScriptBindPacket(uint playerActorId) - { - List lParams; - lParams = LuaUtils.CreateLuaParamList(classPath, false, true, zoneName, "/Area/Zone/ZoneDefault", -1, (byte)1, true, false, false, false, false, false, false, false); - return ActorInstantiatePacket.BuildPacket(actorId, playerActorId, actorName, "ZoneDefault", lParams); - } - - public override BasePacket GetSpawnPackets(uint playerActorId) - { - List subpackets = new List(); - subpackets.Add(CreateAddActorPacket(playerActorId, 0)); - subpackets.Add(CreateSpeedPacket(playerActorId)); - subpackets.Add(CreateSpawnPositonPacket(playerActorId, 0x1)); - subpackets.Add(CreateNamePacket(playerActorId)); - subpackets.Add(CreateStatePacket(playerActorId)); - subpackets.Add(CreateIsZoneingPacket(playerActorId)); - subpackets.Add(CreateScriptBindPacket(playerActorId)); - return BasePacket.CreatePacket(subpackets, true, false); - } - - #region Actor Management - - public void AddActorToZone(Actor actor) - { - if (!mActorList.ContainsKey(actor.actorId)) - mActorList.Add(actor.actorId, actor); - - int gridX = (int)actor.positionX / boundingGridSize; - int gridY = (int)actor.positionZ / boundingGridSize; - - gridX += halfWidth; - gridY += halfHeight; - - //Boundries - if (gridX < 0) - gridX = 0; - if (gridX >= numXBlocks) - gridX = numXBlocks - 1; - if (gridY < 0) - gridY = 0; - if (gridY >= numYBlocks) - gridY = numYBlocks - 1; - - lock (mActorBlock) - mActorBlock[gridX, gridY].Add(actor); - } - - public void RemoveActorFromZone(Actor actor) - { - mActorList.Remove(actor.actorId); - - int gridX = (int)actor.positionX / boundingGridSize; - int gridY = (int)actor.positionZ / boundingGridSize; - - gridX += halfWidth; - gridY += halfHeight; - - //Boundries - if (gridX < 0) - gridX = 0; - if (gridX >= numXBlocks) - gridX = numXBlocks - 1; - if (gridY < 0) - gridY = 0; - if (gridY >= numYBlocks) - gridY = numYBlocks - 1; - - lock (mActorBlock) - mActorBlock[gridX, gridY].Remove(actor); - } - - public void UpdateActorPosition(Actor actor) - { - int gridX = (int)actor.positionX / boundingGridSize; - int gridY = (int)actor.positionZ / boundingGridSize; - - gridX += halfWidth; - gridY += halfHeight; - - //Boundries - if (gridX < 0) - gridX = 0; - if (gridX >= numXBlocks) - gridX = numXBlocks - 1; - if (gridY < 0) - gridY = 0; - if (gridY >= numYBlocks) - gridY = numYBlocks - 1; - - int gridOldX = (int)actor.oldPositionX / boundingGridSize; - int gridOldY = (int)actor.oldPositionZ / boundingGridSize; - - gridOldX += halfWidth; - gridOldY += halfHeight; - - //Boundries - if (gridOldX < 0) - gridOldX = 0; - if (gridOldX >= numXBlocks) - gridOldX = numXBlocks - 1; - if (gridOldY < 0) - gridOldY = 0; - if (gridOldY >= numYBlocks) - gridOldY = numYBlocks - 1; - - //Still in same block - if (gridX == gridOldX && gridY == gridOldY) - return; - - lock (mActorBlock) - { - mActorBlock[gridOldX, gridOldY].Remove(actor); - mActorBlock[gridX, gridY].Add(actor); - } - } - - public List GetActorsAroundPoint(float x, float y, int checkDistance) - { - checkDistance /= boundingGridSize; - - int gridX = (int)x/boundingGridSize; - int gridY = (int)y/boundingGridSize; - - gridX += halfWidth; - gridY += halfHeight; - - //Boundries - if (gridX < 0) - gridX = 0; - if (gridX >= numXBlocks) - gridX = numXBlocks - 1; - if (gridY < 0) - gridY = 0; - if (gridY >= numYBlocks) - gridY = numYBlocks - 1; - - List result = new List(); - - for (int gx = gridX - checkDistance; gx <= gridX + checkDistance; gx++) - { - for (int gy = gridY - checkDistance; gy <= gridY + checkDistance; gy++) - { - result.AddRange(mActorBlock[gx, gy]); - } - } - - //Remove players if isolation zone - if (isIsolated) - { - for (int i = 0; i < result.Count; i++) - { - if (result[i] is Player) - result.RemoveAt(i); - } - } - - return result; - } - - public List GetActorsAroundActor(Actor actor, int checkDistance) - { - checkDistance /= boundingGridSize; - - int gridX = (int)actor.positionX / boundingGridSize; - int gridY = (int)actor.positionZ / boundingGridSize; - - gridX += halfWidth; - gridY += halfHeight; - - //Boundries - if (gridX < 0) - gridX = 0; - if (gridX >= numXBlocks) - gridX = numXBlocks - 1; - if (gridY < 0) - gridY = 0; - if (gridY >= numYBlocks) - gridY = numYBlocks - 1; - - List result = new List(); - - for (int gy = ((gridY - checkDistance) < 0 ? 0 : (gridY - checkDistance)); gy <= ((gridY + checkDistance) >= numYBlocks ? numYBlocks - 1 : (gridY + checkDistance)); gy++) - { - for (int gx = ((gridX - checkDistance) < 0 ? 0 : (gridX - checkDistance)); gx <= ((gridX + checkDistance) >= numXBlocks ? numXBlocks - 1 : (gridX + checkDistance)); gx++) - { - result.AddRange(mActorBlock[gx, gy]); - } - } - - //Remove players if isolation zone - if (isIsolated) - { - for (int i = 0; i < result.Count; i++) - { - if (result[i] is Player) - result.RemoveAt(i); - } - } - - return result; - } - - #endregion - - public Actor FindActorInZone(uint id) - { - if (!mActorList.ContainsKey(id)) - return null; - return mActorList[id]; + protected List mSpawnLocations = new List(); + protected Dictionary mActorList = new Dictionary(); + protected List[,] mActorBlock; + + LuaScript 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) + { + + this.zoneName = zoneName; + this.regionId = regionId; + this.canStealth = canStealth; + this.isIsolated = isIsolated; + this.isInn = isInn; + this.canRideChocobo = canRideChocobo; + this.isInstanceRaid = isInstanceRaid; + + this.bgmDay = bgmDay; + this.bgmNight = bgmNight; + this.bgmBattle = bgmBattle; + + this.displayNameId = 0; + this.customDisplayName = "_areaMaster"; + this.actorName = String.Format("_areaMaster@{0:X5}",id<<8); + + this.className = className; + + numXBlocks = (maxX - minX) / boundingGridSize; + numYBlocks = (maxY - minY) / boundingGridSize; + mActorBlock = new List[numXBlocks, numYBlocks]; + halfWidth = numXBlocks / 2; + halfHeight = numYBlocks / 2; + + for (int y = 0; y < numYBlocks; y++) + { + for (int x = 0; x < numXBlocks; x++ ) + { + mActorBlock[x, y] = new List(); + } + } + + } + + public override SubPacket CreateScriptBindPacket(uint playerActorId) + { + List lParams; + lParams = LuaUtils.CreateLuaParamList(classPath, false, true, zoneName, "/Area/Zone/ZoneDefault", -1, (byte)1, true, false, false, false, false, false, false, false); + return ActorInstantiatePacket.BuildPacket(actorId, playerActorId, actorName, "ZoneDefault", lParams); + } + + public override BasePacket GetSpawnPackets(uint playerActorId) + { + List subpackets = new List(); + subpackets.Add(CreateAddActorPacket(playerActorId, 0)); + subpackets.Add(CreateSpeedPacket(playerActorId)); + subpackets.Add(CreateSpawnPositonPacket(playerActorId, 0x1)); + subpackets.Add(CreateNamePacket(playerActorId)); + subpackets.Add(CreateStatePacket(playerActorId)); + subpackets.Add(CreateIsZoneingPacket(playerActorId)); + subpackets.Add(CreateScriptBindPacket(playerActorId)); + return BasePacket.CreatePacket(subpackets, true, false); + } + + #region Actor Management + + public void AddActorToZone(Actor actor) + { + if (!mActorList.ContainsKey(actor.actorId)) + mActorList.Add(actor.actorId, actor); + + int gridX = (int)actor.positionX / boundingGridSize; + int gridY = (int)actor.positionZ / boundingGridSize; + + gridX += halfWidth; + gridY += halfHeight; + + //Boundries + if (gridX < 0) + gridX = 0; + if (gridX >= numXBlocks) + gridX = numXBlocks - 1; + if (gridY < 0) + gridY = 0; + if (gridY >= numYBlocks) + gridY = numYBlocks - 1; + + lock (mActorBlock) + mActorBlock[gridX, gridY].Add(actor); + } + + public void RemoveActorFromZone(Actor actor) + { + mActorList.Remove(actor.actorId); + + int gridX = (int)actor.positionX / boundingGridSize; + int gridY = (int)actor.positionZ / boundingGridSize; + + gridX += halfWidth; + gridY += halfHeight; + + //Boundries + if (gridX < 0) + gridX = 0; + if (gridX >= numXBlocks) + gridX = numXBlocks - 1; + if (gridY < 0) + gridY = 0; + if (gridY >= numYBlocks) + gridY = numYBlocks - 1; + + lock (mActorBlock) + mActorBlock[gridX, gridY].Remove(actor); + } + + public void UpdateActorPosition(Actor actor) + { + int gridX = (int)actor.positionX / boundingGridSize; + int gridY = (int)actor.positionZ / boundingGridSize; + + gridX += halfWidth; + gridY += halfHeight; + + //Boundries + if (gridX < 0) + gridX = 0; + if (gridX >= numXBlocks) + gridX = numXBlocks - 1; + if (gridY < 0) + gridY = 0; + if (gridY >= numYBlocks) + gridY = numYBlocks - 1; + + int gridOldX = (int)actor.oldPositionX / boundingGridSize; + int gridOldY = (int)actor.oldPositionZ / boundingGridSize; + + gridOldX += halfWidth; + gridOldY += halfHeight; + + //Boundries + if (gridOldX < 0) + gridOldX = 0; + if (gridOldX >= numXBlocks) + gridOldX = numXBlocks - 1; + if (gridOldY < 0) + gridOldY = 0; + if (gridOldY >= numYBlocks) + gridOldY = numYBlocks - 1; + + //Still in same block + if (gridX == gridOldX && gridY == gridOldY) + return; + + lock (mActorBlock) + { + mActorBlock[gridOldX, gridOldY].Remove(actor); + mActorBlock[gridX, gridY].Add(actor); + } + } + + public List GetActorsAroundPoint(float x, float y, int checkDistance) + { + checkDistance /= boundingGridSize; + + int gridX = (int)x/boundingGridSize; + int gridY = (int)y/boundingGridSize; + + gridX += halfWidth; + gridY += halfHeight; + + //Boundries + if (gridX < 0) + gridX = 0; + if (gridX >= numXBlocks) + gridX = numXBlocks - 1; + if (gridY < 0) + gridY = 0; + if (gridY >= numYBlocks) + gridY = numYBlocks - 1; + + List result = new List(); + + for (int gx = gridX - checkDistance; gx <= gridX + checkDistance; gx++) + { + for (int gy = gridY - checkDistance; gy <= gridY + checkDistance; gy++) + { + result.AddRange(mActorBlock[gx, gy]); + } + } + + //Remove players if isolation zone + if (isIsolated) + { + for (int i = 0; i < result.Count; i++) + { + if (result[i] is Player) + result.RemoveAt(i); + } + } + + return result; + } + + public List GetActorsAroundActor(Actor actor, int checkDistance) + { + checkDistance /= boundingGridSize; + + int gridX = (int)actor.positionX / boundingGridSize; + int gridY = (int)actor.positionZ / boundingGridSize; + + gridX += halfWidth; + gridY += halfHeight; + + //Boundries + if (gridX < 0) + gridX = 0; + if (gridX >= numXBlocks) + gridX = numXBlocks - 1; + if (gridY < 0) + gridY = 0; + if (gridY >= numYBlocks) + gridY = numYBlocks - 1; + + List result = new List(); + + for (int gy = ((gridY - checkDistance) < 0 ? 0 : (gridY - checkDistance)); gy <= ((gridY + checkDistance) >= numYBlocks ? numYBlocks - 1 : (gridY + checkDistance)); gy++) + { + for (int gx = ((gridX - checkDistance) < 0 ? 0 : (gridX - checkDistance)); gx <= ((gridX + checkDistance) >= numXBlocks ? numXBlocks - 1 : (gridX + checkDistance)); gx++) + { + result.AddRange(mActorBlock[gx, gy]); + } + } + + //Remove players if isolation zone + if (isIsolated) + { + for (int i = 0; i < result.Count; i++) + { + if (result[i] is Player) + result.RemoveAt(i); + } + } + + return result; + } + + #endregion + + public Actor FindActorInArea(uint id) + { + if (!mActorList.ContainsKey(id)) + return null; + return mActorList[id]; } public Actor FindActorInZoneByUniqueID(string uniqueId) { - foreach (Actor a in mActorList.Values) - { - if (a is Npc) + foreach (Actor a in mActorList.Values) + { + if (a is Npc) { - if (((Npc)a).GetUniqueId().ToLower().Equals(uniqueId)) - return a; - } - } + if (((Npc)a).GetUniqueId().ToLower().Equals(uniqueId)) + return a; + } + } return null; - } - - public Player FindPCInZone(string name) - { - foreach (Actor a in mActorList.Values) - { - if (a is Player) - { - if (((Player)a).customDisplayName.ToLower().Equals(name.ToLower())) - return (Player)a; - } - } - return null; - } - - public Player FindPCInZone(uint id) - { - if (!mActorList.ContainsKey(id)) - return null; - return (Player)mActorList[id]; - } - - public void Clear() - { - //Clear All - mActorList.Clear(); - for (int y = 0; y < numYBlocks; y++) - { - for (int x = 0; x < numXBlocks; x++) - { - mActorBlock[x, y].Clear(); - } - } - } - - public void BroadcastPacketAroundActor(Actor actor, SubPacket packet) - { - if (isIsolated) - return; - - List aroundActor = GetActorsAroundActor(actor, 50); - foreach (Actor a in aroundActor) - { - if (a is Player) - { - if (isIsolated && packet.header.sourceId != a.actorId) - continue; - - SubPacket clonedPacket = new SubPacket(packet, actor.actorId); - Player p = (Player)a; - p.QueuePacket(clonedPacket); - } - } - } - - public void SpawnActor(SpawnLocation location) - { - ActorClass actorClass = Server.GetWorldManager().GetActorClass(location.classId); - - if (actorClass == null) + } + + public Player FindPCInZone(string name) + { + foreach (Actor a in mActorList.Values) + { + if (a is Player) + { + if (((Player)a).customDisplayName.ToLower().Equals(name.ToLower())) + return (Player)a; + } + } + return null; + } + + public Player FindPCInZone(uint id) + { + if (!mActorList.ContainsKey(id)) + return null; + return (Player)mActorList[id]; + } + + public void Clear() + { + //Clear All + mActorList.Clear(); + for (int y = 0; y < numYBlocks; y++) + { + for (int x = 0; x < numXBlocks; x++) + { + mActorBlock[x, y].Clear(); + } + } + } + + public void BroadcastPacketAroundActor(Actor actor, SubPacket packet) + { + if (isIsolated) + return; + + List aroundActor = GetActorsAroundActor(actor, 50); + foreach (Actor a in aroundActor) + { + if (a is Player) + { + if (isIsolated && packet.header.sourceId != a.actorId) + continue; + + SubPacket clonedPacket = new SubPacket(packet, actor.actorId); + Player p = (Player)a; + p.QueuePacket(clonedPacket); + } + } + } + + public void SpawnActor(SpawnLocation location) + { + ActorClass actorClass = Server.GetWorldManager().GetActorClass(location.classId); + + if (actorClass == null) return; uint zoneId; @@ -378,21 +373,44 @@ namespace FFXIVClassic_Map_Server.Actors if (this is PrivateArea) zoneId = ((PrivateArea)this).GetParentZone().actorId; else - zoneId = actorId; - - Npc npc = new Npc(mActorList.Count + 1, actorClass, location.uniqueId, this, location.x, location.y, location.z, location.rot, location.state, location.animId, null); - - - npc.LoadEventConditions(actorClass.eventConditions); - - AddActorToZone(npc); - } - + zoneId = actorId; + + Npc npc = new Npc(mActorList.Count + 1, actorClass, location.uniqueId, this, location.x, location.y, location.z, location.rot, location.state, location.animId, null); + + + npc.LoadEventConditions(actorClass.eventConditions); + + AddActorToZone(npc); + } + + public Npc SpawnActor(uint classId, string uniqueId, float x, float y, float z, float rot = 0, ushort state = 0, uint animId = 0) + { + ActorClass actorClass = Server.GetWorldManager().GetActorClass(classId); + + if (actorClass == null) + return null; + + uint zoneId; + + if (this is PrivateArea) + zoneId = ((PrivateArea)this).GetParentZone().actorId; + else + zoneId = actorId; + + Npc npc = new Npc(mActorList.Count + 1, actorClass, uniqueId, this, x, y, z, rot, state, animId, null); + + npc.LoadEventConditions(actorClass.eventConditions); + + AddActorToZone(npc); + + return npc; + } + public Director GetWeatherDirector() { return mWeatherDirector; - } - + } + public void ChangeWeather(ushort weather, ushort transitionTime, Player player, bool zoneWide = false) { weatherNormal = weather; @@ -412,32 +430,7 @@ namespace FFXIVClassic_Map_Server.Actors } } } - } - - public void CreateContentGroup(uint[] initialMembers) - { - lock (groupLock) - { - ContentGroup contentGroup = new ContentGroup(groupIndexId, initialMembers == null ? new uint[0] : initialMembers); - mContentGroups.Add(groupIndexId, contentGroup); - groupIndexId++; - if (initialMembers != null && initialMembers.Length != 0) - contentGroup.SendAll(); - } - } - - public void DeleteContentGroup(ulong groupId) - { - lock (groupLock) - { - if (mContentGroups.ContainsKey(groupId) && mContentGroups[groupId] is ContentGroup) - { - ContentGroup group = (ContentGroup) mContentGroups[groupId]; - group.SendDeletePackets(); - mContentGroups.Remove(groupId); - } - } - } + } public Director CreateDirector(string path) { @@ -472,7 +465,7 @@ namespace FFXIVClassic_Map_Server.Actors return currentDirectors[id]; return null; } - + public void Update(double deltaTime) { lock (mActorList) @@ -480,6 +473,7 @@ namespace FFXIVClassic_Map_Server.Actors foreach (Actor a in mActorList.Values) a.Update(deltaTime); } - } - } -} + } + + } +} diff --git a/FFXIVClassic Map Server/actors/area/PrivateAreaContent.cs b/FFXIVClassic Map Server/actors/area/PrivateAreaContent.cs new file mode 100644 index 00000000..bec669f6 --- /dev/null +++ b/FFXIVClassic Map Server/actors/area/PrivateAreaContent.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace FFXIVClassic_Map_Server.actors.area +{ + class PrivateAreaContent : PrivateArea + { + public PrivateAreaContent(Zone parent, uint id, string className, string privateAreaName, uint privateAreaType) + : base(parent, id, className, privateAreaName, privateAreaType, 0, 0, 0) + { + } + } +} diff --git a/FFXIVClassic Map Server/actors/area/Zone.cs b/FFXIVClassic Map Server/actors/area/Zone.cs index c3509458..81fc0c3a 100644 --- a/FFXIVClassic Map Server/actors/area/Zone.cs +++ b/FFXIVClassic Map Server/actors/area/Zone.cs @@ -16,7 +16,7 @@ namespace FFXIVClassic_Map_Server.actors.area class Zone : Area { Dictionary> privateAreas = new Dictionary>(); - Dictionary> instancedPrivateAreas = new Dictionary>(); + Dictionary> contentAreas = new Dictionary>(); public Zone(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, zoneName, regionId, className, bgmDay, bgmNight, bgmBattle, isIsolated, isInn, canRideChocobo, canStealth, isInstanceRaid) @@ -93,5 +93,28 @@ namespace FFXIVClassic_Map_Server.actors.area } } + public Actor FindActorInZone(uint id) + { + if (!mActorList.ContainsKey(id)) + { + foreach(Dictionary paList in privateAreas.Values) + { + foreach(PrivateArea pa in paList.Values) + { + Actor actor = pa.FindActorInArea(id); + if (actor != null) + return actor; + } + } + return null; + } + else + return mActorList[id]; + } + + public void CreateContentArea() + { + + } } } diff --git a/FFXIVClassic Map Server/actors/chara/Character.cs b/FFXIVClassic Map Server/actors/chara/Character.cs index 34d8850c..0d6f6c00 100644 --- a/FFXIVClassic Map Server/actors/chara/Character.cs +++ b/FFXIVClassic Map Server/actors/chara/Character.cs @@ -3,6 +3,7 @@ using FFXIVClassic.Common; using FFXIVClassic_Map_Server.actors.group; using FFXIVClassic_Map_Server.Actors.Chara; using FFXIVClassic_Map_Server.packets.send.actor; +using FFXIVClassic_Map_Server.utils; namespace FFXIVClassic_Map_Server.Actors { @@ -87,6 +88,18 @@ namespace FFXIVClassic_Map_Server.Actors player.QueuePacket(SetActorQuestGraphicPacket.BuildPacket(player.actorId, actorId, graphicNum)); } + public void SetCurrentContentGroup(uint groupType, Player player = null) + { + charaWork.currentContentGroup = groupType; + + if (player != null) + { + ActorPropertyPacketUtil propPacketUtil = new ActorPropertyPacketUtil("charaWork/currentContentGroup", this, actorId); + propPacketUtil.AddProperty("charaWork.currentContentGroup"); + player.QueuePackets(propPacketUtil.Done()); + } + } + } } diff --git a/FFXIVClassic Map Server/actors/chara/npc/Npc.cs b/FFXIVClassic Map Server/actors/chara/npc/Npc.cs index 358a55cd..50a221b9 100644 --- a/FFXIVClassic Map Server/actors/chara/npc/Npc.cs +++ b/FFXIVClassic Map Server/actors/chara/npc/Npc.cs @@ -206,6 +206,9 @@ namespace FFXIVClassic_Map_Server.Actors { ActorPropertyPacketUtil propPacketUtil = new ActorPropertyPacketUtil("/_init", this, playerActorId); + //Potential + propPacketUtil.AddProperty("charaWork.battleSave.potencial"); + //Properties for (int i = 0; i < charaWork.property.Length; i++) { diff --git a/FFXIVClassic Map Server/actors/chara/npc/NpcWork.cs b/FFXIVClassic Map Server/actors/chara/npc/NpcWork.cs index 7b70d017..ca21d437 100644 --- a/FFXIVClassic Map Server/actors/chara/npc/NpcWork.cs +++ b/FFXIVClassic Map Server/actors/chara/npc/NpcWork.cs @@ -5,6 +5,6 @@ public ushort pushCommand; public int pushCommandSub; public byte pushCommandPriority; - public byte hateType; + public byte hateType = 1; } } diff --git a/FFXIVClassic Map Server/actors/chara/player/Player.cs b/FFXIVClassic Map Server/actors/chara/player/Player.cs index 508d0448..cbd1f8b0 100644 --- a/FFXIVClassic Map Server/actors/chara/player/Player.cs +++ b/FFXIVClassic Map Server/actors/chara/player/Player.cs @@ -552,7 +552,6 @@ namespace FFXIVClassic_Map_Server.Actors director.GetSpawnPackets(actorId).DebugPrintPacket(); QueuePacket(director.GetSpawnPackets(actorId)); QueuePacket(director.GetInitPackets(actorId)); - //QueuePacket(director.GetSetEventStatusPackets(actorId)); } } @@ -634,7 +633,7 @@ namespace FFXIVClassic_Map_Server.Actors public void ChangeAnimation(uint animId) { - Actor a = zone.FindActorInZone(currentTarget); + Actor a = zone.FindActorInArea(currentTarget); if (a is Npc) ((Npc)a).animationId = animId; } @@ -1323,6 +1322,13 @@ namespace FFXIVClassic_Map_Server.Actors } } + public void SendDirectorPackets(Director director) + { + director.GetSpawnPackets(actorId).DebugPrintPacket(); + QueuePacket(director.GetSpawnPackets(actorId)); + QueuePacket(director.GetInitPackets(actorId)); + } + public void RemoveDirector(Director director) { if (!ownedDirectors.Contains(director)) diff --git a/FFXIVClassic Map Server/actors/group/ContentGroup.cs b/FFXIVClassic Map Server/actors/group/ContentGroup.cs index 9599733c..5f84f2ab 100644 --- a/FFXIVClassic Map Server/actors/group/ContentGroup.cs +++ b/FFXIVClassic Map Server/actors/group/ContentGroup.cs @@ -1,7 +1,11 @@ using FFXIVClassic.Common; +using FFXIVClassic_Map_Server.actors.director; +using FFXIVClassic_Map_Server.actors.group.Work; +using FFXIVClassic_Map_Server.Actors; using FFXIVClassic_Map_Server.dataobjects; using FFXIVClassic_Map_Server.packets.send.group; using FFXIVClassic_Map_Server.packets.send.groups; +using FFXIVClassic_Map_Server.utils; using System; using System.Collections.Generic; using System.Linq; @@ -12,20 +16,36 @@ namespace FFXIVClassic_Map_Server.actors.group { class ContentGroup : Group { + public ContentGroupWork contentGroupWork = new ContentGroupWork(); + private Director director; private List members = new List(); - public ContentGroup(ulong groupIndex, uint[] initialMembers) : base(groupIndex) + public ContentGroup(ulong groupIndex, Director director, uint[] initialMembers) : base(groupIndex) { - for (int i = 0; i < initialMembers.Length; i++) - members.Add(initialMembers[i]); + if (initialMembers != null) + { + for (int i = 0; i < initialMembers.Length; i++) + members.Add(initialMembers[i]); + } + + this.director = director; + contentGroupWork._globalTemp.director = (ulong)director.actorId << 32; } - public void AddMember(uint memberId) + public void AddMember(Actor actor) { - members.Add(memberId); + if (actor == null) + return; + + members.Add(actor.actorId); + if (actor is Character) + { + ((Character)actor).SetCurrentContentGroup(GetTypeId()); + SendCurrentContentSync(actor); + } SendGroupPacketsAll(members); } - + public void RemoveMember(uint memberId) { members.Remove(memberId); @@ -44,18 +64,68 @@ namespace FFXIVClassic_Map_Server.actors.group return groupMembers; } + public override int GetMemberCount() + { + return members.Count; + } + public override void SendInitWorkValues(Session session) { - SynchGroupWorkValuesPacket groupWork = new SynchGroupWorkValuesPacket(groupIndex); + SynchGroupWorkValuesPacket groupWork = new SynchGroupWorkValuesPacket(groupIndex); + groupWork.addProperty(this, "contentGroupWork._globalTemp.director"); + groupWork.addByte(Utils.MurmurHash2("contentGroupWork.property[0]", 0), 1); groupWork.setTarget("/_init"); SubPacket test = groupWork.buildPacket(session.id, session.id); + test.DebugPrintSubPacket(); session.QueuePacket(test, true, false); } + public override void SendGroupPackets(Session session) + { + ulong time = Utils.MilisUnixTimeStampUTC(); + List members = BuildMemberList(session.id); + + session.QueuePacket(GroupHeaderPacket.buildPacket(session.id, session.GetActor().zoneId, time, this), true, false); + session.QueuePacket(GroupMembersBeginPacket.buildPacket(session.id, session.GetActor().zoneId, time, this), true, false); + + int currentIndex = 0; + + while (true) + { + if (GetMemberCount() - currentIndex >= 64) + session.QueuePacket(ContentMembersX64Packet.buildPacket(session.id, session.GetActor().zoneId, time, members, ref currentIndex), true, false); + else if (GetMemberCount() - currentIndex >= 32) + session.QueuePacket(ContentMembersX32Packet.buildPacket(session.id, session.GetActor().zoneId, time, members, ref currentIndex), true, false); + else if (GetMemberCount() - currentIndex >= 16) + session.QueuePacket(ContentMembersX16Packet.buildPacket(session.id, session.GetActor().zoneId, time, members, ref currentIndex), true, false); + else if (GetMemberCount() - currentIndex > 0) + session.QueuePacket(ContentMembersX08Packet.buildPacket(session.id, session.GetActor().zoneId, time, members, ref currentIndex), true, false); + else + break; + } + + session.QueuePacket(GroupMembersEndPacket.buildPacket(session.id, session.GetActor().zoneId, time, this), true, false); + + } + + public void SendCurrentContentSync(Actor currentContentChanged) + { + foreach (uint memberId in members) + { + Session session = Server.GetServer().GetSession(memberId); + if (session != null) + { + ActorPropertyPacketUtil propPacketUtil = new ActorPropertyPacketUtil("charaWork/currentContentGroup", currentContentChanged, session.id); + propPacketUtil.AddProperty("charaWork.currentContentGroup"); + session.GetActor().QueuePackets(propPacketUtil.Done()); + } + } + } + public override uint GetTypeId() { - return Group.ContentGroup_SimpleContentGroup24A; + return Group.ContentGroup_SimpleContentGroup24B; } diff --git a/FFXIVClassic Map Server/actors/group/Group.cs b/FFXIVClassic Map Server/actors/group/Group.cs index d8bbd366..d2d6bdec 100644 --- a/FFXIVClassic Map Server/actors/group/Group.cs +++ b/FFXIVClassic Map Server/actors/group/Group.cs @@ -115,7 +115,7 @@ namespace FFXIVClassic_Map_Server.actors.group } } - public void SendGroupPackets(Session session) + public virtual void SendGroupPackets(Session session) { ulong time = Utils.MilisUnixTimeStampUTC(); List members = BuildMemberList(session.id); diff --git a/FFXIVClassic Map Server/actors/group/work/ContentGroupWork.cs b/FFXIVClassic Map Server/actors/group/work/ContentGroupWork.cs new file mode 100644 index 00000000..9029474f --- /dev/null +++ b/FFXIVClassic Map Server/actors/group/work/ContentGroupWork.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace FFXIVClassic_Map_Server.actors.group.Work +{ + class ContentGroupWork + { + public GlobalTemp _globalTemp = new GlobalTemp(); + public bool[] property = new bool[32]; + } +} diff --git a/FFXIVClassic Map Server/actors/group/work/ContentWork.cs b/FFXIVClassic Map Server/actors/group/work/GlobalTemp.cs similarity index 56% rename from FFXIVClassic Map Server/actors/group/work/ContentWork.cs rename to FFXIVClassic Map Server/actors/group/work/GlobalTemp.cs index 773b4022..4faf2864 100644 --- a/FFXIVClassic Map Server/actors/group/work/ContentWork.cs +++ b/FFXIVClassic Map Server/actors/group/work/GlobalTemp.cs @@ -2,12 +2,11 @@ using System.Collections.Generic; using System.Linq; using System.Text; -using System.Threading.Tasks; namespace FFXIVClassic_Map_Server.actors.group.Work { - class ContentWork + class GlobalTemp { - public GroupGlobalTemp _globalTemp = new GroupGlobalTemp(); + public ulong director; } } diff --git a/FFXIVClassic Map Server/packets/send/groups/ContentMembersX08Packet.cs b/FFXIVClassic Map Server/packets/send/groups/ContentMembersX08Packet.cs new file mode 100644 index 00000000..6f0f9a21 --- /dev/null +++ b/FFXIVClassic Map Server/packets/send/groups/ContentMembersX08Packet.cs @@ -0,0 +1,51 @@ +using FFXIVClassic.Common; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace FFXIVClassic_Map_Server.packets.send.group +{ + class ContentMembersX08Packet + { + public const ushort OPCODE = 0x0183; + public const uint PACKET_SIZE = 0x1B8; + + public static SubPacket buildPacket(uint playerActorID, uint locationCode, ulong sequenceId, List entries, ref int offset) + { + byte[] data = new byte[PACKET_SIZE - 0x20]; + + using (MemoryStream mem = new MemoryStream(data)) + { + using (BinaryWriter binWriter = new BinaryWriter(mem)) + { + //Write List Header + binWriter.Write((UInt64)locationCode); + binWriter.Write((UInt64)sequenceId); + //Write Entries + int max = 8; + if (entries.Count-offset < 8) + max = entries.Count - offset; + for (int i = 0; i < max; i++) + { + binWriter.Seek(0x10 + (0xC * i), SeekOrigin.Begin); + + GroupMember entry = entries[i]; + binWriter.Write((UInt32)entry.actorId); + binWriter.Write((UInt32)1001); //Layout ID + binWriter.Write((UInt32)1); //? + + offset++; + } + //Write Count + binWriter.Seek(0x10 + (0xC * 8), SeekOrigin.Begin); + binWriter.Write(max); + } + } + + return new SubPacket(OPCODE, playerActorID, playerActorID, data); + } + } +} diff --git a/FFXIVClassic Map Server/packets/send/groups/ContentMembersX16Packet.cs b/FFXIVClassic Map Server/packets/send/groups/ContentMembersX16Packet.cs new file mode 100644 index 00000000..60ef9692 --- /dev/null +++ b/FFXIVClassic Map Server/packets/send/groups/ContentMembersX16Packet.cs @@ -0,0 +1,48 @@ +using FFXIVClassic.Common; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace FFXIVClassic_Map_Server.packets.send.group +{ + class ContentMembersX16Packet + { + public const ushort OPCODE = 0x0184; + public const uint PACKET_SIZE = 0xF0; + + public static SubPacket buildPacket(uint playerActorID, uint locationCode, ulong sequenceId, List entries, ref int offset) + { + byte[] data = new byte[PACKET_SIZE - 0x20]; + + using (MemoryStream mem = new MemoryStream(data)) + { + using (BinaryWriter binWriter = new BinaryWriter(mem)) + { + //Write List Header + binWriter.Write((UInt64)locationCode); + binWriter.Write((UInt64)sequenceId); + //Write Entries + int max = 16; + if (entries.Count-offset < 16) + max = entries.Count - offset; + for (int i = 0; i < max; i++) + { + binWriter.Seek(0x10 + (0xC * i), SeekOrigin.Begin); + + GroupMember entry = entries[i]; + binWriter.Write((UInt32)entry.actorId); + binWriter.Write((UInt32)1001); + binWriter.Write((UInt32)1); + + offset++; + } + } + } + + return new SubPacket(OPCODE, playerActorID, playerActorID, data); + } + } +} diff --git a/FFXIVClassic Map Server/packets/send/groups/ContentMembersX32Packet.cs b/FFXIVClassic Map Server/packets/send/groups/ContentMembersX32Packet.cs new file mode 100644 index 00000000..532b0c1c --- /dev/null +++ b/FFXIVClassic Map Server/packets/send/groups/ContentMembersX32Packet.cs @@ -0,0 +1,48 @@ +using FFXIVClassic.Common; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace FFXIVClassic_Map_Server.packets.send.group +{ + class ContentMembersX32Packet + { + public const ushort OPCODE = 0x0185; + public const uint PACKET_SIZE = 0x1B0; + + public static SubPacket buildPacket(uint playerActorID, uint locationCode, ulong sequenceId, List entries, ref int offset) + { + byte[] data = new byte[PACKET_SIZE - 0x20]; + + using (MemoryStream mem = new MemoryStream(data)) + { + using (BinaryWriter binWriter = new BinaryWriter(mem)) + { + //Write List Header + binWriter.Write((UInt64)locationCode); + binWriter.Write((UInt64)sequenceId); + //Write Entries + int max = 32; + if (entries.Count-offset < 32) + max = entries.Count - offset; + for (int i = 0; i < max; i++) + { + binWriter.Seek(0x10 + (0xC * i), SeekOrigin.Begin); + + GroupMember entry = entries[i]; + binWriter.Write((UInt32)entry.actorId); + binWriter.Write((UInt32)1001); + binWriter.Write((UInt32)1); + + offset++; + } + } + } + + return new SubPacket(OPCODE, playerActorID, playerActorID, data); + } + } +} diff --git a/FFXIVClassic Map Server/packets/send/groups/ContentMembersX64Packet.cs b/FFXIVClassic Map Server/packets/send/groups/ContentMembersX64Packet.cs new file mode 100644 index 00000000..f8da3882 --- /dev/null +++ b/FFXIVClassic Map Server/packets/send/groups/ContentMembersX64Packet.cs @@ -0,0 +1,48 @@ +using FFXIVClassic.Common; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace FFXIVClassic_Map_Server.packets.send.group +{ + class ContentMembersX64Packet + { + public const ushort OPCODE = 0x0186; + public const uint PACKET_SIZE = 0x330; + + public static SubPacket buildPacket(uint playerActorID, uint locationCode, ulong sequenceId, List entries, ref int offset) + { + byte[] data = new byte[PACKET_SIZE - 0x20]; + + using (MemoryStream mem = new MemoryStream(data)) + { + using (BinaryWriter binWriter = new BinaryWriter(mem)) + { + //Write List Header + binWriter.Write((UInt64)locationCode); + binWriter.Write((UInt64)sequenceId); + //Write Entries + int max = 64; + if (entries.Count - offset < 64) + max = entries.Count - offset; + for (int i = 0; i < max; i++) + { + binWriter.Seek(0x10 + (0xC * i), SeekOrigin.Begin); + + GroupMember entry = entries[i]; + binWriter.Write((UInt32)entry.actorId); + binWriter.Write((UInt32)1001); + binWriter.Write((UInt32)1); + + offset++; + } + } + } + + return new SubPacket(OPCODE, playerActorID, playerActorID, data); + } + } +} diff --git a/FFXIVClassic Map Server/packets/send/groups/GroupMember.cs b/FFXIVClassic Map Server/packets/send/groups/GroupMember.cs index 1a00c38f..819d47ea 100644 --- a/FFXIVClassic Map Server/packets/send/groups/GroupMember.cs +++ b/FFXIVClassic Map Server/packets/send/groups/GroupMember.cs @@ -18,7 +18,7 @@ namespace FFXIVClassic_Map_Server.packets.send.group this.unknown2 = unknown2; this.flag1 = flag1; this.isOnline = isOnline; - this.name = name; + this.name = name == null ? "" : name; } } } diff --git a/FFXIVClassic Map Server/packets/send/groups/GroupMembersX08Packet.cs b/FFXIVClassic Map Server/packets/send/groups/GroupMembersX08Packet.cs index e5272adb..badf0f11 100644 --- a/FFXIVClassic Map Server/packets/send/groups/GroupMembersX08Packet.cs +++ b/FFXIVClassic Map Server/packets/send/groups/GroupMembersX08Packet.cs @@ -38,6 +38,7 @@ namespace FFXIVClassic_Map_Server.packets.send.group binWriter.Write((UInt32)entry.unknown2); binWriter.Write((Byte)(entry.flag1? 1 : 0)); binWriter.Write((Byte)(entry.isOnline? 1 : 0)); + binWriter.Write(Encoding.ASCII.GetBytes(entry.name), 0, Encoding.ASCII.GetByteCount(entry.name) >= 0x20 ? 0x20 : Encoding.ASCII.GetByteCount(entry.name)); offset++; diff --git a/FFXIVClassic Map Server/packets/send/groups/SynchGroupWorkValuesPacket.cs b/FFXIVClassic Map Server/packets/send/groups/SynchGroupWorkValuesPacket.cs index 8959175e..fbe2e2d6 100644 --- a/FFXIVClassic Map Server/packets/send/groups/SynchGroupWorkValuesPacket.cs +++ b/FFXIVClassic Map Server/packets/send/groups/SynchGroupWorkValuesPacket.cs @@ -108,7 +108,7 @@ namespace FFXIVClassic_Map_Server.packets.send.groups string[] split = name.Split('.'); int arrayIndex = 0; - if (!(split[0].Equals("work") || split[0].Equals("partyGroupWork"))) + if (!(split[0].Equals("work") || split[0].Equals("partyGroupWork") || split[0].Equals("contentGroupWork"))) return; Object curObj = group;