diff --git a/FFXIVClassic Map Server/FFXIVClassic Map Server.csproj b/FFXIVClassic Map Server/FFXIVClassic Map Server.csproj
index 321f8949..eb57d7d8 100644
--- a/FFXIVClassic Map Server/FFXIVClassic Map Server.csproj
+++ b/FFXIVClassic Map Server/FFXIVClassic Map Server.csproj
@@ -289,6 +289,7 @@
+
diff --git a/FFXIVClassic Map Server/WorldManager.cs b/FFXIVClassic Map Server/WorldManager.cs
index 17c034a8..897a0ab1 100644
--- a/FFXIVClassic Map Server/WorldManager.cs
+++ b/FFXIVClassic Map Server/WorldManager.cs
@@ -708,6 +708,12 @@ namespace FFXIVClassic_Map_Server
Server.GetWorldConnection().QueuePacket(packet, true, false);
}
+ public void RequestWorldLinkshellChangeActive(Player player, string lsname)
+ {
+ SubPacket packet = LinkshellChangePacket.BuildPacket(player.playerSession, lsname);
+ Server.GetWorldConnection().QueuePacket(packet, true, false);
+ }
+
private void RequestWorldServerZoneChange(Player player, uint destinationZoneId, byte spawnType, float spawnX, float spawnY, float spawnZ, float spawnRotation)
{
ZoneConnection zc = Server.GetWorldConnection();
diff --git a/FFXIVClassic Map Server/packets/WorldPackets/Send/Group/LinkshellChangePacket.cs b/FFXIVClassic Map Server/packets/WorldPackets/Send/Group/LinkshellChangePacket.cs
new file mode 100644
index 00000000..ec256612
--- /dev/null
+++ b/FFXIVClassic Map Server/packets/WorldPackets/Send/Group/LinkshellChangePacket.cs
@@ -0,0 +1,30 @@
+using FFXIVClassic.Common;
+using FFXIVClassic_Map_Server.Actors;
+using FFXIVClassic_Map_Server.dataobjects;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Text;
+
+namespace FFXIVClassic_Map_Server.packets.WorldPackets.Send.Group
+{
+ class LinkshellChangePacket
+ {
+ public const ushort OPCODE = 0x1028;
+ public const uint PACKET_SIZE = 0x48;
+
+ public static SubPacket BuildPacket(Session session, string lsName)
+ {
+ byte[] data = new byte[PACKET_SIZE - 0x20];
+ using (MemoryStream mem = new MemoryStream(data))
+ {
+ using (BinaryWriter binWriter = new BinaryWriter(mem))
+ {
+ binWriter.Write(Encoding.ASCII.GetBytes(lsName), 0, Encoding.ASCII.GetByteCount(lsName) >= 0x20 ? 0x20 : Encoding.ASCII.GetByteCount(lsName));
+ }
+ }
+ return new SubPacket(true, OPCODE, session.id, session.id, data);
+ }
+
+ }
+}
diff --git a/FFXIVClassic World Server/DataObjects/Session.cs b/FFXIVClassic World Server/DataObjects/Session.cs
index d90a3f1d..966d3248 100644
--- a/FFXIVClassic World Server/DataObjects/Session.cs
+++ b/FFXIVClassic World Server/DataObjects/Session.cs
@@ -1,4 +1,7 @@
-using FFXIVClassic_World_Server.Packets.Send.Subpackets;
+using FFXIVClassic.Common;
+using FFXIVClassic_World_Server.DataObjects.Group;
+using FFXIVClassic_World_Server.Packets.Send.Subpackets;
+using FFXIVClassic_World_Server.Packets.Send.Subpackets.Groups;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -16,7 +19,7 @@ namespace FFXIVClassic_World_Server.DataObjects
public string characterName;
public uint currentZoneId;
- public uint activeLinkshellIndex = 0;
+ public string activeLinkshellName = "";
public readonly ClientConnection clientConnection;
public readonly Channel type;
@@ -28,7 +31,7 @@ namespace FFXIVClassic_World_Server.DataObjects
this.clientConnection = connection;
this.type = type;
connection.owner = this;
- Database.LoadZoneSessionInfo(this);
+ Database.LoadZoneSessionInfo(this);
}
public void SendGameMessage(uint actorId, ushort textId, byte log, params object[] msgParams)
@@ -65,5 +68,15 @@ namespace FFXIVClassic_World_Server.DataObjects
clientConnection.QueuePacket(GameMessagePacket.BuildPacket(0x5FF80001, sessionId, 0x5FF80001, textId, displayId, log, LuaUtils.CreateLuaParamList(msgParams)), true, false);
}
+
+ public bool SetActiveLS(string name)
+ {
+ if (Database.SetActiveLS(this, name))
+ {
+ activeLinkshellName = name;
+ return true;
+ }
+ return false;
+ }
}
}
diff --git a/FFXIVClassic World Server/Database.cs b/FFXIVClassic World Server/Database.cs
index e5a3993c..0a63ac4e 100644
--- a/FFXIVClassic World Server/Database.cs
+++ b/FFXIVClassic World Server/Database.cs
@@ -45,7 +45,7 @@ namespace FFXIVClassic_World_Server
public static bool LoadZoneSessionInfo(Session session)
{
- string characterName;
+ string characterName, currentLinkshell;
uint currentZone = 0;
uint destinationZone = 0;
bool readIn = false;
@@ -55,7 +55,7 @@ namespace FFXIVClassic_World_Server
try
{
conn.Open();
- MySqlCommand cmd = new MySqlCommand("SELECT name, currentZoneId, destinationZoneId FROM characters WHERE id = @charaId", conn);
+ MySqlCommand cmd = new MySqlCommand("SELECT name, currentZoneId, destinationZoneId, currentActiveLinkshell FROM characters WHERE id = @charaId", conn);
cmd.Parameters.AddWithValue("@charaId", session.sessionId);
using (MySqlDataReader Reader = cmd.ExecuteReader())
{
@@ -64,9 +64,11 @@ namespace FFXIVClassic_World_Server
characterName = Reader.GetString("name");
currentZone = Reader.GetUInt32("currentZoneId");
destinationZone = Reader.GetUInt32("destinationZoneId");
+ currentLinkshell = Reader.GetString("currentActiveLinkshell");
session.characterName = characterName;
session.currentZoneId = currentZone;
+ session.activeLinkshellName = currentLinkshell;
readIn = true;
}
@@ -501,5 +503,31 @@ namespace FFXIVClassic_World_Server
}
return success;
}
+
+ public static bool SetActiveLS(Session session, string name)
+ {
+ bool success = 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("UPDATE characters SET currentActiveLinkshell = @lsName WHERE id = @charaId", conn);
+ cmd.Parameters.AddWithValue("@charaId", session.sessionId);
+ cmd.Parameters.AddWithValue("@lsName", name);
+ cmd.ExecuteNonQuery();
+ success = true;
+ }
+ catch (MySqlException e)
+ {
+ Program.Log.Error(e.ToString());
+ }
+ finally
+ {
+ conn.Dispose();
+ }
+ }
+ return success;
+ }
}
}
diff --git a/FFXIVClassic World Server/FFXIVClassic World Server.csproj b/FFXIVClassic World Server/FFXIVClassic World Server.csproj
index 0d94415c..995f8ba5 100644
--- a/FFXIVClassic World Server/FFXIVClassic World Server.csproj
+++ b/FFXIVClassic World Server/FFXIVClassic World Server.csproj
@@ -112,6 +112,7 @@
+
@@ -128,12 +129,12 @@
+
-
diff --git a/FFXIVClassic World Server/Packets/Send/Subpackets/Groups/SetActiveLinkshellPacket.cs b/FFXIVClassic World Server/Packets/Send/Subpackets/Groups/SetActiveLinkshellPacket.cs
new file mode 100644
index 00000000..8c21a21b
--- /dev/null
+++ b/FFXIVClassic World Server/Packets/Send/Subpackets/Groups/SetActiveLinkshellPacket.cs
@@ -0,0 +1,42 @@
+using FFXIVClassic.Common;
+using FFXIVClassic_World_Server.DataObjects.Group;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace FFXIVClassic_World_Server.Packets.Send.Subpackets.Groups
+{
+ class SetActiveLinkshellPacket
+ {
+
+ public const ushort OPCODE = 0x018A;
+ public const uint PACKET_SIZE = 0x98;
+
+ public static SubPacket BuildPacket(uint sessionId, ulong groupId)
+ {
+ byte[] data = new byte[PACKET_SIZE - 0x20];
+
+ using (MemoryStream mem = new MemoryStream(data))
+ {
+ using (BinaryWriter binWriter = new BinaryWriter(mem))
+ {
+ //Write the LS groupId
+ binWriter.Write((UInt64)groupId);
+
+ //Write the LS group type
+ binWriter.Seek(0x40, SeekOrigin.Begin);
+ binWriter.Write((UInt32)Group.CompanyGroup);
+
+ //Seems to be a index but can only set one active so /shrug
+ binWriter.Seek(0x60, SeekOrigin.Begin);
+ binWriter.Write((UInt32)(groupId == 0 ? 0 : 1));
+ }
+ }
+
+ return new SubPacket(OPCODE, sessionId, sessionId, data);
+ }
+ }
+}
diff --git a/FFXIVClassic World Server/Packets/WorldPackets/Receive/Group/SetActiveLinkshellPacket.cs b/FFXIVClassic World Server/Packets/WorldPackets/Receive/Group/LinkshellChangePacket.cs
similarity index 72%
rename from FFXIVClassic World Server/Packets/WorldPackets/Receive/Group/SetActiveLinkshellPacket.cs
rename to FFXIVClassic World Server/Packets/WorldPackets/Receive/Group/LinkshellChangePacket.cs
index 2f689b2c..99b8bd7f 100644
--- a/FFXIVClassic World Server/Packets/WorldPackets/Receive/Group/SetActiveLinkshellPacket.cs
+++ b/FFXIVClassic World Server/Packets/WorldPackets/Receive/Group/LinkshellChangePacket.cs
@@ -5,13 +5,13 @@ using System.Text;
namespace FFXIVClassic_World_Server.Packets.WorldPackets.Receive.Group
{
- class SetActiveLinkshellPacket
+ class LinkshellChangePacket
{
public bool invalidPacket = false;
+
+ public string lsName;
- public string name;
-
- public SetActiveLinkshellPacket(byte[] data)
+ public LinkshellChangePacket(byte[] data)
{
using (MemoryStream mem = new MemoryStream(data))
{
@@ -19,7 +19,7 @@ namespace FFXIVClassic_World_Server.Packets.WorldPackets.Receive.Group
{
try
{
- name = Encoding.ASCII.GetString(binReader.ReadBytes(0x20)).Trim(new[] { '\0' });
+ lsName = Encoding.ASCII.GetString(binReader.ReadBytes(0x20)).Trim(new[] { '\0' });
}
catch (Exception)
{
diff --git a/FFXIVClassic World Server/Server.cs b/FFXIVClassic World Server/Server.cs
index 642bc604..557ed80a 100644
--- a/FFXIVClassic World Server/Server.cs
+++ b/FFXIVClassic World Server/Server.cs
@@ -302,8 +302,8 @@ namespace FFXIVClassic_World_Server
break;
//Linkshell set active
case 0x1028:
- SetActiveLinkshellPacket setActiveLinkshellPacket = new SetActiveLinkshellPacket(subpacket.data);
- //Linkshell ls = mWorldManager.GetLinkshellManager().GetLinkshell();
+ LinkshellChangePacket linkshellChangePacket = new LinkshellChangePacket(subpacket.data);
+ mWorldManager.ProcessLinkshellSetActive(GetSession(subpacket.header.sourceId), linkshellChangePacket.lsName);
break;
//Linkshell invite member
case 0x1029:
diff --git a/FFXIVClassic World Server/WorldMaster.cs b/FFXIVClassic World Server/WorldMaster.cs
index 0e588559..01cdddab 100644
--- a/FFXIVClassic World Server/WorldMaster.cs
+++ b/FFXIVClassic World Server/WorldMaster.cs
@@ -219,7 +219,25 @@ namespace FFXIVClassic_World_Server
mRetainerGroupManager.GetRetainerGroup(session.sessionId).SendGroupPackets(session);
List linkshells = mLinkshellManager.GetPlayerLinkshellMembership(session.sessionId);
foreach (Linkshell ls in linkshells)
- ls.SendGroupPackets(session);
+ ls.SendGroupPackets(session);
+
+ //Reset to blank if in unknown state
+ ulong activeGroupIndex = 0;
+ if (!session.activeLinkshellName.Equals(""))
+ {
+ Linkshell activeLs = mLinkshellManager.GetLinkshell(session.activeLinkshellName);
+ if (activeLs != null && activeLs.HasMember(session.sessionId))
+ {
+ activeGroupIndex = activeLs.groupIndex;
+ }
+ else
+ {
+ session.activeLinkshellName = "";
+ Database.SetActiveLS(session, "");
+ }
+ }
+ SubPacket activeLsPacket = SetActiveLinkshellPacket.BuildPacket(session.sessionId, activeGroupIndex);
+ session.clientConnection.QueuePacket(activeLsPacket, true, false);
}
private void SendMotD(Session session)
@@ -408,6 +426,34 @@ namespace FFXIVClassic_World_Server
relation.SendDeletePackets(inviterSession.sessionId, inviteeSession.sessionId);
}
+ public void ProcessLinkshellSetActive(Session requestSession, string lsName)
+ {
+ //Deactivate all
+ if (lsName.Equals(""))
+ {
+ requestSession.SetActiveLS(lsName);
+ SubPacket activeLsPacket = SetActiveLinkshellPacket.BuildPacket(requestSession.sessionId, 0);
+ requestSession.clientConnection.QueuePacket(activeLsPacket, true, false);
+ requestSession.SendGameMessage(25132, 0x20, (object)1);
+ }
+ else
+ {
+ Linkshell ls = mLinkshellManager.GetLinkshell(lsName);
+
+ if (ls == null || !ls.HasMember(requestSession.sessionId))
+ {
+ requestSession.SendGameMessage(25167, 0x20, (object)1, (object)lsName);
+ }
+ else
+ {
+ requestSession.SetActiveLS(lsName);
+ SubPacket activeLsPacket = SetActiveLinkshellPacket.BuildPacket(requestSession.sessionId, ls.groupIndex);
+ requestSession.clientConnection.QueuePacket(activeLsPacket, true, false);
+ requestSession.SendGameMessage(25131, 0x20, (object)1, (object)lsName);
+ }
+ }
+ }
+
public void IncrementGroupIndex()
{
mRunningGroupIndex++;
diff --git a/data/scripts/commands/LinkshellChangeCommand.lua b/data/scripts/commands/LinkshellChangeCommand.lua
new file mode 100644
index 00000000..25022775
--- /dev/null
+++ b/data/scripts/commands/LinkshellChangeCommand.lua
@@ -0,0 +1,15 @@
+--[[
+
+LinkshellChangeCommand Script
+
+--]]
+
+function onEventStarted(player, actor, triggerName, linkshellName, arg1, arg2)
+
+ if (linkshellName == nil) then
+ linkshellName = "";
+ end
+ GetWorldManager():RequestWorldLinkshellChangeActive(player, linkshellName);
+ player:EndEvent();
+
+end
\ No newline at end of file