diff --git a/FFXIVClassic Map Server/Database.cs b/FFXIVClassic Map Server/Database.cs index dddeea59..3b621095 100644 --- a/FFXIVClassic Map Server/Database.cs +++ b/FFXIVClassic Map Server/Database.cs @@ -543,51 +543,5 @@ namespace FFXIVClassic_Lobby_Server return cheevosPacket.buildPacket(player.actorId); } - public static void loadZones(Efficient32bitHashTable zoneList) - { - int count = 0; - 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(); - - string query = @" - SELECT - id, - regionId, - zoneName, - dayMusic, - nightMusic, - battleMusic, - isInn, - canRideChocobo, - canStealth, - isInstanceRaid - FROM server_zones - WHERE zoneName IS NOT NULL"; - - MySqlCommand cmd = new MySqlCommand(query, conn); - - using (MySqlDataReader reader = cmd.ExecuteReader()) - { - while (reader.Read()) { - Zone zone = new Zone(reader.GetUInt32(0), reader.GetString(2), reader.GetUInt16(1), reader.GetUInt16(3), reader.GetUInt16(4), reader.GetUInt16(5), reader.GetBoolean(6), reader.GetBoolean(7), reader.GetBoolean(8), reader.GetBoolean(9)); - zoneList.Add(zone.actorId, zone); - count++; - } - } - } - catch (MySqlException e) - { Console.WriteLine(e); } - finally - { - conn.Dispose(); - } - } - - Log.info(String.Format("Loaded {0} zones.", count)); - } - } } diff --git a/FFXIVClassic Map Server/FFXIVClassic Map Server.csproj b/FFXIVClassic Map Server/FFXIVClassic Map Server.csproj index 42f3674e..7c7ff0b3 100644 --- a/FFXIVClassic Map Server/FFXIVClassic Map Server.csproj +++ b/FFXIVClassic Map Server/FFXIVClassic Map Server.csproj @@ -213,6 +213,7 @@ + diff --git a/FFXIVClassic Map Server/PacketProcessor.cs b/FFXIVClassic Map Server/PacketProcessor.cs index f622e2e4..8087f98f 100644 --- a/FFXIVClassic Map Server/PacketProcessor.cs +++ b/FFXIVClassic Map Server/PacketProcessor.cs @@ -42,23 +42,16 @@ namespace FFXIVClassic_Lobby_Server { class PacketProcessor { + Server mServer; LuaEngine luaEngine = new LuaEngine(); Dictionary mPlayers; List mConnections; - StaticActors mStaticActors = new StaticActors(); - - DebugProg debug = new DebugProg(); - WorldMaster worldMaster = new WorldMaster(); - - Efficient32bitHashTable zoneList = new Efficient32bitHashTable(); - - public PacketProcessor(Dictionary playerList, List connectionList) + public PacketProcessor(Server server, Dictionary playerList, List connectionList) { mPlayers = playerList; mConnections = connectionList; - - Database.loadZones(zoneList); + mServer = server; } public void processPacket(ClientConnection client, BasePacket packet) @@ -191,7 +184,7 @@ namespace FFXIVClassic_Lobby_Server //Unknown case 0x0002: - player.getActor().zone = zoneList.Get(player.getActor().zoneId); + player.getActor().zone = mServer.GetWorldManager().GetZone(player.getActor().zoneId); BasePacket reply9 = new BasePacket("./packets/login/login9_zonesetup.bin"); //Bed, Book created BasePacket reply10 = new BasePacket("./packets/login/login10.bin"); //Item Storage, Inn Door created @@ -305,8 +298,8 @@ namespace FFXIVClassic_Lobby_Server player.getActor().zone.addActorToZone(player.getActor()); BasePacket innSpawn = player.getActor().zone.getSpawnPackets(player.actorID); - BasePacket debugSpawn = debug.getSpawnPackets(player.actorID); - BasePacket worldMasterSpawn = worldMaster.getSpawnPackets(player.actorID); + BasePacket debugSpawn = mServer.GetWorldManager().GetDebugActor().getSpawnPackets(player.actorID); + BasePacket worldMasterSpawn = mServer.GetWorldManager().GetActor().getSpawnPackets(player.actorID); innSpawn.debugPrintPacket(); client.queuePacket(innSpawn); diff --git a/FFXIVClassic Map Server/Server.cs b/FFXIVClassic Map Server/Server.cs index 59add233..0d2118cf 100644 --- a/FFXIVClassic Map Server/Server.cs +++ b/FFXIVClassic Map Server/Server.cs @@ -11,6 +11,8 @@ using FFXIVClassic_Map_Server.dataobjects; using FFXIVClassic_Lobby_Server.packets; using System.IO; using FFXIVClassic_Map_Server.packets.send.actor; +using FFXIVClassic_Map_Server; +using FFXIVClassic_Map_Server.actors; namespace FFXIVClassic_Lobby_Server { @@ -24,6 +26,8 @@ namespace FFXIVClassic_Lobby_Server private Dictionary mConnectedPlayerList = new Dictionary(); private List mConnectionList = new List(); + private WorldManager mWorldManager; + private StaticActors mStaticActors = new StaticActors(); private PacketProcessor mProcessor; private Thread mProcessorThread; private Thread mGameThread; @@ -31,6 +35,9 @@ namespace FFXIVClassic_Lobby_Server #region Socket Handling public bool startServer() { + mWorldManager = new WorldManager(); + mWorldManager.LoadZoneList(); + IPEndPoint serverEndPoint = new System.Net.IPEndPoint(IPAddress.Parse(ConfigConstants.OPTIONS_BINDIP), FFXIV_MAP_PORT); try{ @@ -63,7 +70,7 @@ namespace FFXIVClassic_Lobby_Server Console.WriteLine("{0}:{1}", (mServerSocket.LocalEndPoint as IPEndPoint).Address, (mServerSocket.LocalEndPoint as IPEndPoint).Port); Console.ForegroundColor = ConsoleColor.Gray; - mProcessor = new PacketProcessor(mConnectedPlayerList, mConnectionList); + mProcessor = new PacketProcessor(this, mConnectedPlayerList, mConnectionList); //mGameThread = new Thread(new ThreadStart(mProcessor.update)); //mGameThread.Start(); @@ -274,5 +281,11 @@ namespace FFXIVClassic_Lobby_Server mProcessor.doWarp(Convert.ToUInt32(map), Single.Parse(x), Single.Parse(y), Single.Parse(z)); } + public WorldManager GetWorldManager() + { + return mWorldManager; + } + } + } diff --git a/FFXIVClassic Map Server/WorldManager.cs b/FFXIVClassic Map Server/WorldManager.cs new file mode 100644 index 00000000..27bd8875 --- /dev/null +++ b/FFXIVClassic Map Server/WorldManager.cs @@ -0,0 +1,245 @@ +using FFXIVClassic_Lobby_Server; +using FFXIVClassic_Lobby_Server.common; +using FFXIVClassic_Map_Server.actors; +using FFXIVClassic_Map_Server.actors.debug; +using FFXIVClassic_Map_Server.actors.world; +using FFXIVClassic_Map_Server.common.EfficientHashTables; +using FFXIVClassic_Map_Server.dataobjects; +using FFXIVClassic_Map_Server.dataobjects.chara; +using FFXIVClassic_Map_Server.packets.send; +using MySql.Data.MySqlClient; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace FFXIVClassic_Map_Server +{ + class WorldManager + { + private DebugProg debug = new DebugProg(); + private WorldMaster worldMaster = new WorldMaster(); + private Dictionary zoneList; + private Dictionary zoneEntranceList; + + public void LoadZoneList() + { + zoneList = new Dictionary(); + int count = 0; + 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(); + + string query = @" + SELECT + id, + regionId, + zoneName, + dayMusic, + nightMusic, + battleMusic, + isInn, + canRideChocobo, + canStealth, + isInstanceRaid + FROM server_zones + WHERE zoneName IS NOT NULL"; + + MySqlCommand cmd = new MySqlCommand(query, conn); + + using (MySqlDataReader reader = cmd.ExecuteReader()) + { + while (reader.Read()) + { + Zone zone = new Zone(reader.GetUInt32(0), reader.GetString(2), reader.GetUInt16(1), reader.GetUInt16(3), reader.GetUInt16(4), reader.GetUInt16(5), reader.GetBoolean(6), reader.GetBoolean(7), reader.GetBoolean(8), reader.GetBoolean(9)); + zoneList[zone.actorId] = zone; + count++; + } + } + } + catch (MySqlException e) + { Console.WriteLine(e); } + finally + { + conn.Dispose(); + } + } + + Log.info(String.Format("Loaded {0} zones.", count)); + } + + public void LoadZoneEntranceList() + { + zoneEntranceList = new Dictionary(); + int count = 0; + 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(); + + string query = @" + SELECT + id, + zoneId, + spawnType, + spawnX, + spawnY, + spawnZ, + spawnRotation + FROM server_zones_spawnlocations"; + + MySqlCommand cmd = new MySqlCommand(query, conn); + + using (MySqlDataReader reader = cmd.ExecuteReader()) + { + while (reader.Read()) + { + uint id = reader.GetUInt32(0); + ZoneEntrance entance = new ZoneEntrance(reader.GetUInt32(1), reader.GetByte(2), reader.GetFloat(3), reader.GetFloat(4), reader.GetFloat(5), reader.GetFloat(6)); + zoneEntranceList[id] = entance; + count++; + } + } + } + catch (MySqlException e) + { Console.WriteLine(e); } + finally + { + conn.Dispose(); + } + } + + Log.info(String.Format("Loaded {0} zone spawn locations.", count)); + } + + //Moves the actor to the new zone if exists. No packets are sent nor position changed. + public void DoSeamlessZoneChange(Player player, uint destinationZoneId) + { + Zone oldZone; + + if (player.zone != null) + { + oldZone = player.zone; + oldZone.removeActorToZone(player); + } + + //Add player to new zone and update + Zone newZone = GetZone(destinationZoneId); + + //This server does not contain that zoneId + if (newZone == null) + return; + + newZone.addActorToZone(player); + } + + //Moves actor to new zone, and sends packets to spawn at the given zone entrance + public void DoTeleportZoneChange(Player player, uint destinationZoneId, uint zoneEntrance) + { + if (!zoneEntranceList.ContainsKey(zoneEntrance)) + { + Log.error("Given zone entrance was not found: " + zoneEntrance); + return; + } + + ZoneEntrance ze = zoneEntranceList[zoneEntrance]; + DoTeleportZoneChange(player, destinationZoneId, ze.spawnType, ze.spawnX, ze.spawnY, ze.spawnZ, ze.spawnRotation); + } + + //Moves actor to new zone, and sends packets to spawn at the given coords. + public void DoTeleportZoneChange(Player player, uint destinationZoneId, byte spawnType, float spawnX, float spawnY, float spawnZ, float spawnRotation) + { + Zone oldZone; + + //Remove player from currentZone if transfer else it's login + if (player.zone != null) + { + oldZone = player.zone; + oldZone.removeActorToZone(player); + } + + //Add player to new zone and update + Zone newZone = GetZone(destinationZoneId); + + //This server does not contain that zoneId + if (newZone == null) + return; + + newZone.addActorToZone(player); + + //Update player actor's properties + player.zoneId = newZone.actorId; + player.zone = newZone; + player.positionX = spawnX; + player.positionY = spawnY; + player.positionZ = spawnZ; + player.rotation = spawnRotation; + + //Send packets + player.sendZoneInPackets(this); + } + + public Player GetPCInWorld(string name) + { + foreach (Zone zone in zoneList.Values) + { + Player p = zone.FindPCInZone(name); + if (p != null) + return p; + } + return null; + } + + public Player GetPCInWorld(uint charId) + { + foreach (Zone zone in zoneList.Values) + { + Player p = zone.FindPCInZone(charId); + if (p != null) + return p; + } + return null; + } + + public Zone GetZone(uint zoneId) + { + return zoneList[zoneId]; + } + + public WorldMaster GetActor() + { + return worldMaster; + } + + public DebugProg GetDebugActor() + { + return debug; + } + + public class ZoneEntrance + { + public uint zoneId; + public byte spawnType; + public float spawnX; + public float spawnY; + public float spawnZ; + public float spawnRotation; + + public ZoneEntrance(uint zoneId, byte spawnType, float x, float y, float z, float rot) + { + this.zoneId = zoneId; + this.spawnType = spawnType; + this.spawnX = x; + this.spawnY = y; + this.spawnZ = z; + this.spawnRotation = rot; + } + } + + } + +} diff --git a/FFXIVClassic Map Server/actors/area/Zone.cs b/FFXIVClassic Map Server/actors/area/Zone.cs index 5dcfea5d..6339e278 100644 --- a/FFXIVClassic Map Server/actors/area/Zone.cs +++ b/FFXIVClassic Map Server/actors/area/Zone.cs @@ -1,6 +1,7 @@ using FFXIVClassic_Lobby_Server.common; using FFXIVClassic_Lobby_Server.packets; using FFXIVClassic_Map_Server.dataobjects; +using FFXIVClassic_Map_Server.dataobjects.chara; using FFXIVClassic_Map_Server.lua; using FFXIVClassic_Map_Server.packets.send.actor; using System; @@ -23,7 +24,9 @@ namespace FFXIVClassic_Map_Server public int minX = -100, minY = -100, maxX = 100, maxY = 100; private int numXBlocks, numYBlocks; private int halfWidth, halfHeight; - private List[,] actorBlock; + + private Dictionary mActorList = new Dictionary(); + private List[,] mActorBlock; public Zone(uint id, string zoneName, ushort regionId, ushort bgmDay, ushort bgmNight, ushort bgmBattle, bool canStealth, bool isInn, bool canRideChocobo, bool isInstanceRaid) : base(id) @@ -48,7 +51,7 @@ namespace FFXIVClassic_Map_Server numXBlocks = (maxX - minX) / boundingGridSize; numYBlocks = (maxY - minY) / boundingGridSize; - actorBlock = new List[numXBlocks, numYBlocks]; + mActorBlock = new List[numXBlocks, numYBlocks]; halfWidth = numXBlocks / 2; halfHeight = numYBlocks / 2; @@ -56,7 +59,7 @@ namespace FFXIVClassic_Map_Server { for (int x = 0; x < numXBlocks; x++ ) { - actorBlock[x, y] = new List(); + mActorBlock[x, y] = new List(); } } @@ -86,6 +89,8 @@ namespace FFXIVClassic_Map_Server public void addActorToZone(Actor actor) { + mActorList.Add(actor.actorId, actor); + int gridX = (int)actor.positionX / boundingGridSize; int gridY = (int)actor.positionZ / boundingGridSize; @@ -102,12 +107,14 @@ namespace FFXIVClassic_Map_Server if (gridY >= numYBlocks) gridY = numYBlocks - 1; - lock (actorBlock) - actorBlock[gridX, gridY].Add(actor); + lock (mActorBlock) + mActorBlock[gridX, gridY].Add(actor); } public void removeActorToZone(Actor actor) { + mActorList.Remove(actor.actorId); + int gridX = (int)actor.positionX / boundingGridSize; int gridY = (int)actor.positionZ / boundingGridSize; @@ -124,8 +131,8 @@ namespace FFXIVClassic_Map_Server if (gridY >= numYBlocks) gridY = numYBlocks - 1; - lock (actorBlock) - actorBlock[gridX, gridY].Remove(actor); + lock (mActorBlock) + mActorBlock[gridX, gridY].Remove(actor); } public void updateActorPosition(Actor actor) @@ -166,10 +173,10 @@ namespace FFXIVClassic_Map_Server if (gridX == gridOldX && gridY == gridOldY) return; - lock (actorBlock) - actorBlock[gridOldX, gridOldY].Remove(actor); - lock (actorBlock) - actorBlock[gridX, gridY].Add(actor); + lock (mActorBlock) + mActorBlock[gridOldX, gridOldY].Remove(actor); + lock (mActorBlock) + mActorBlock[gridX, gridY].Add(actor); } public List getActorsAroundPoint(float x, float y, int checkDistance) @@ -196,7 +203,7 @@ namespace FFXIVClassic_Map_Server { for (int gy = gridY - checkDistance; gy <= gridY + checkDistance; gy++) { - result.AddRange(actorBlock[gx, gy]); + result.AddRange(mActorBlock[gx, gy]); } } @@ -219,7 +226,7 @@ namespace FFXIVClassic_Map_Server { for (int gx = ((gridX - checkDistance) < 0 ? 0 : (gridX - checkDistance)); gx <= ((gridX + checkDistance) >= numXBlocks ? numXBlocks - 1 : (gridX + checkDistance)); gx++) { - result.AddRange(actorBlock[gx, gy]); + result.AddRange(mActorBlock[gx, gy]); } } @@ -227,5 +234,26 @@ namespace FFXIVClassic_Map_Server } #endregion + + public Player FindPCInZone(string name) + { + foreach (Actor a in mActorList.Values) + { + if (a is Player) + { + if (((Player)a).customDisplayName.Equals(name)) + return (Player)a; + } + } + return null; + } + + public Player FindPCInZone(uint id) + { + if (!mActorList.ContainsKey(id)) + return null; + return (Player)mActorList[id]; + } + } } diff --git a/FFXIVClassic Map Server/actors/chara/player/Player.cs b/FFXIVClassic Map Server/actors/chara/player/Player.cs index b350a3fa..ed0c4b9a 100644 --- a/FFXIVClassic Map Server/actors/chara/player/Player.cs +++ b/FFXIVClassic Map Server/actors/chara/player/Player.cs @@ -2,6 +2,7 @@ using FFXIVClassic_Lobby_Server.common; using FFXIVClassic_Lobby_Server.packets; using FFXIVClassic_Map_Server.lua; +using FFXIVClassic_Map_Server.packets.send; using FFXIVClassic_Map_Server.packets.send.actor; using FFXIVClassic_Map_Server.packets.send.player; using FFXIVClassic_Map_Server.utils; @@ -356,6 +357,27 @@ namespace FFXIVClassic_Map_Server.dataobjects.chara return propPacketUtil.done(); } + public void sendZoneInPackets(WorldManager world) + { + ClientConnection client; + client.queuePacket(SetMapPacket.buildPacket(actorId, zone.regionId, zone.actorId), true, false); + //client.queuePacket(_0x2Packet.buildPacket(player.actorID), true, false); + client.queuePacket(SetMusicPacket.buildPacket(actorId, 0x3D, 0x01), true, false); + client.queuePacket(SetWeatherPacket.buildPacket(actorId, SetWeatherPacket.WEATHER_CLEAR), true, false); + + client.queuePacket(getSpawnPackets(actorId)); + client.queuePacket(getInitPackets(actorId)); + + + + BasePacket innSpawn = zone.getSpawnPackets(actorId); + BasePacket debugSpawn = world.GetDebugActor().getSpawnPackets(actorId); + BasePacket worldMasterSpawn = world.GetActor().getSpawnPackets(actorId); + client.queuePacket(innSpawn); + client.queuePacket(debugSpawn); + client.queuePacket(worldMasterSpawn); + } + public bool isMyPlayer(uint otherActorId) { return actorId == otherActorId; diff --git a/FFXIVClassic Map Server/common/EfficientHashTables.cs b/FFXIVClassic Map Server/common/EfficientHashTables.cs index 3f94c510..9ac2c4aa 100644 --- a/FFXIVClassic Map Server/common/EfficientHashTables.cs +++ b/FFXIVClassic Map Server/common/EfficientHashTables.cs @@ -154,6 +154,8 @@ namespace FFXIVClassic_Map_Server.common return r; } + + } }