diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index e0e89768..00000000 --- a/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "FFXIVClassic Map Server/navmesh/rcdtcs"] - path = FFXIVClassic Map Server/navmesh/rcdtcs - url = https://github.com/jlalleve/rcdtcs diff --git a/FFXIVClassic Common Class Lib/Utils.cs b/FFXIVClassic Common Class Lib/Utils.cs index 469213ca..18e46adb 100644 --- a/FFXIVClassic Common Class Lib/Utils.cs +++ b/FFXIVClassic Common Class Lib/Utils.cs @@ -352,16 +352,31 @@ namespace FFXIVClassic.Common return (value >> bits) | (value << (16 - bits)); } - public static float Clamp(float val, float min, float max) + public static T Clamp(this T value, T min, T max) where T : IComparable { - - return Math.Max(Math.Min(max, val), min); + if (value.CompareTo(min) < 0) + return min; + else if (value.CompareTo(max) > 0) + return max; + else + return value; } - public static int Clamp(int val, int min, int max) + public static T Min(this T value, T min) where T : IComparable + { + if (value.CompareTo(min) > 0) + return min; + else + return value; + } + + public static T Max(this T value, T max) where T : IComparable { - return Math.Max(Math.Min(max, val), min); + if (value.CompareTo(max) < 0) + return max; + else + return value; } public static float Distance(float x, float y, float z, float x2, float y2, float z2) diff --git a/FFXIVClassic Map Server/FFXIVClassic Map Server.csproj b/FFXIVClassic Map Server/FFXIVClassic Map Server.csproj index 131d6716..8e3fc9ae 100644 --- a/FFXIVClassic Map Server/FFXIVClassic Map Server.csproj +++ b/FFXIVClassic Map Server/FFXIVClassic Map Server.csproj @@ -69,7 +69,7 @@ False - .\SharpNav.dll + navmesh\SharpNav.dll @@ -137,21 +137,6 @@ - - - - - - - - - - - - - - - @@ -354,7 +339,9 @@ Resources.Designer.cs - + + + xcopy "$(SolutionDir)data\map_config.ini" "$(SolutionDir)$(ProjectName)\$(OutDir)" /d diff --git a/FFXIVClassic Map Server/actors/Actor.cs b/FFXIVClassic Map Server/actors/Actor.cs index 84dd2604..7f85c681 100644 --- a/FFXIVClassic Map Server/actors/Actor.cs +++ b/FFXIVClassic Map Server/actors/Actor.cs @@ -47,6 +47,7 @@ namespace FFXIVClassic_Map_Server.Actors public bool hasMoved = false; public bool isAtSpawn = true; + public EventList eventConditions; public Actor(uint actorId) @@ -82,6 +83,15 @@ namespace FFXIVClassic_Map_Server.Actors } } + public void ResetMoveSpeedsToDefault() + { + this.moveSpeeds[0] = SetActorSpeedPacket.DEFAULT_STOP; + this.moveSpeeds[1] = SetActorSpeedPacket.DEFAULT_WALK; + this.moveSpeeds[2] = SetActorSpeedPacket.DEFAULT_RUN; + this.moveSpeeds[3] = SetActorSpeedPacket.DEFAULT_ACTIVE; + hasMoved = true; + } + public SubPacket CreateAddActorPacket(uint playerActorId, byte val) { return AddActorPacket.BuildPacket(actorId, playerActorId, val); @@ -151,10 +161,6 @@ namespace FFXIVClassic_Map_Server.Actors positionX = pos.X; positionY = pos.Y; positionZ = pos.Z; - - if (target != null) - LookAt(target); - //Program.Server.GetInstance().mLuaEngine.OnPath(actor, position, positionUpdates) } lastMoveUpdate = DateTime.Now; @@ -358,108 +364,9 @@ namespace FFXIVClassic_Map_Server.Actors public void Update(double deltaTime) { - // todo: this is retarded - if (this is Zone || this is Area || this is Player) - return; - - var diffTime = (DateTime.Now - lastAiUpdate); - - // todo: this too - if (diffTime.Milliseconds >= deltaTime) + if (this is Character) { - bool foundActor = false; - bool skipFollow = false; - - // leash back to spawn - if (this.oldPositionX != 0.0f && this.oldPositionY != 0.0f && this.oldPositionZ != 0.0f) - { - var spawnDistance = Utils.Distance(positionX, positionY, positionZ, oldPositionX, oldPositionY, oldPositionZ); - - if (spawnDistance >= 45) - { - skipFollow = true; - } - } - - foreach (var actor in ((Area)zone).GetActorsAroundActor(this, 50)) - { - if (actor is Player && actor != this) - { - var player = actor as Player; - - if (skipFollow) - { - // todo: despawn self for player - - continue; - } - - - var distance = Utils.Distance(positionX, positionY, positionZ, player.positionX, player.positionY, player.positionZ); - - int maxDistance = player == target ? 25 : 15; - - if (distance <= maxDistance) - { - foundActor = true; - - if (!hasMoved) - { - if (distance >= 3) - { - FollowTarget(player, 2.0f); - } - // too close, spread out - else if (distance <= 0.64f) - { - var minRadius = 0.65f; - var maxRadius = 0.85f; - - var angle = Program.Random.NextDouble() * Math.PI * 2; - var radius = Math.Sqrt(Program.Random.NextDouble() * (maxRadius - minRadius)) + minRadius; - - float x = (float)(radius * Math.Cos(angle)); - float z = (float)(radius * Math.Sin(angle)); - - positionUpdates.Add(new utils.Vector3(positionX + x, positionY, positionZ + z)); - - hasMoved = true; - } - - if (target != null) - { - LookAt(target); - } - } - } - break; - } - } - var diffMove = (DateTime.Now - lastMoveUpdate); - - // 5 seconds before back to spawn - if (diffMove.Seconds >= 5 && !foundActor) - { - // leash to spawn - this.isAtSpawn = Utils.Distance(positionX, positionY, positionZ, oldPositionX, oldPositionY, oldPositionZ) <= 25.0f; - - if (this.target == null && skipFollow) - { - // not in spawn range - if (!this.isAtSpawn) - { - PathTo(oldPositionX, oldPositionY, oldPositionZ, 3.0f); - } - // within spawn range, find a random point - else if (diffMove.Seconds >= 15) - { - //Program.Log.Error("Picking random point to walk to! "); - PathTo(oldPositionX, oldPositionY, oldPositionZ, 2.5f, 25, 8.5f); - } - } - this.target = null; - } - lastAiUpdate = DateTime.Now; + ((Character)this).Update(deltaTime); } } @@ -673,88 +580,6 @@ namespace FFXIVClassic_Map_Server.Actors rotation = (float)dRot; } - - public void PathTo(float x, float y, float z, float stepSize = 0.70f, int maxPath = 40, float polyRadius = 0.0f) - { - var pos = new utils.Vector3(positionX, positionY, positionZ); - var dest = new utils.Vector3(x, y, z); - - var sw = new System.Diagnostics.Stopwatch(); - sw.Start(); - - var path = utils.NavmeshUtils.GetPath(((Zone)GetZone()), pos, dest, stepSize, maxPath, polyRadius); - - if (path != null) - { - if (oldPositionX == 0.0f && oldPositionY == 0.0f && oldPositionZ == 0.0f) - { - oldPositionX = positionX; - oldPositionY = positionY; - oldPositionZ = positionZ; - } - - // todo: something went wrong - if (path.Count == 0) - { - positionX = oldPositionX; - positionY = oldPositionY; - positionZ = oldPositionZ; - } - - positionUpdates = path; - - this.hasMoved = true; - this.isAtSpawn = false; - - sw.Stop(); - ((Zone)zone).pathCalls++; - ((Zone)zone).pathCallTime += sw.ElapsedMilliseconds; - Program.Log.Error("[{0}][{1}] Created {2} points in {3} milliseconds", actorId, actorName, path.Count, sw.ElapsedMilliseconds); - } - } - - public void FollowTarget(Actor target, float stepSize = 1.2f, int maxPath = 25) - { - if (target != null) - { - var player = target as Player; - - if (this.target != target) - { - #region super important performance critical code - var chatMode = Program.Random.Next(12); - var emphasis = Program.Random.Next(9); - var drag = Program.Random.Next(7); - - string oni = "ONI"; - string chan = "CHA"; - - for(var i = 0; i < emphasis; ++i) - oni += "I"; - - for (var i = 0; i < drag; ++i) - chan += "A"; - - oni += "-"; - chan += "N"; - - - player.SendMessage((uint)chatMode, "Rowena", oni + chan); - #endregion - - this.target = target; - } - this.moveState = player.moveState; - this.moveSpeeds = player.moveSpeeds; - - PathTo(player.positionX, player.positionY, player.positionZ, stepSize, maxPath); - } - } - - public void OnPath() - { - // todo: lua function onPath in mob script - } } } diff --git a/FFXIVClassic Map Server/actors/area/Area.cs b/FFXIVClassic Map Server/actors/area/Area.cs index df792e0b..bbf8e916 100644 --- a/FFXIVClassic Map Server/actors/area/Area.cs +++ b/FFXIVClassic Map Server/actors/area/Area.cs @@ -202,6 +202,7 @@ namespace FFXIVClassic_Map_Server.Actors mActorBlock[gridOldX, gridOldY].Remove(actor); mActorBlock[gridX, gridY].Add(actor); } + this.hasMoved = true; } public List GetActorsAroundPoint(float x, float y, int checkDistance) diff --git a/FFXIVClassic Map Server/actors/area/Zone.cs b/FFXIVClassic Map Server/actors/area/Zone.cs index a55165c8..5772345a 100644 --- a/FFXIVClassic Map Server/actors/area/Zone.cs +++ b/FFXIVClassic Map Server/actors/area/Zone.cs @@ -22,7 +22,6 @@ namespace FFXIVClassic_Map_Server.actors.area Dictionary> contentAreas = new Dictionary>(); Object contentAreasLock = new Object(); - public Detour.dtNavMesh navMesh; public SharpNav.TiledNavMesh tiledNavMesh; public SharpNav.NavMeshQuery navMeshQuery; public Int64 pathCalls; diff --git a/FFXIVClassic Map Server/actors/chara/Character.cs b/FFXIVClassic Map Server/actors/chara/Character.cs index 849f7b81..424e4e96 100644 --- a/FFXIVClassic Map Server/actors/chara/Character.cs +++ b/FFXIVClassic Map Server/actors/chara/Character.cs @@ -1,9 +1,11 @@  using FFXIVClassic.Common; +using FFXIVClassic_Map_Server.actors.area; using FFXIVClassic_Map_Server.actors.group; using FFXIVClassic_Map_Server.Actors.Chara; using FFXIVClassic_Map_Server.packets.send.actor; using FFXIVClassic_Map_Server.utils; +using System; namespace FFXIVClassic_Map_Server.Actors { @@ -40,6 +42,8 @@ namespace FFXIVClassic_Map_Server.Actors public bool isStatic = false; + public bool isMovingToSpawn = false; + public uint modelId; public uint[] appearanceIds = new uint[28]; @@ -111,6 +115,258 @@ namespace FFXIVClassic_Map_Server.Actors zone.BroadcastPacketAroundActor(this, PlayAnimationOnActorPacket.BuildPacket(actorId, actorId, animId)); } + public void PathTo(float x, float y, float z, float stepSize = 0.70f, int maxPath = 40, float polyRadius = 0.0f) + { + var pos = new utils.Vector3(positionX, positionY, positionZ); + var dest = new utils.Vector3(x, y, z); + + var sw = new System.Diagnostics.Stopwatch(); + sw.Start(); + + var path = utils.NavmeshUtils.GetPath(((Zone)GetZone()), pos, dest, stepSize, maxPath, polyRadius); + + if (path != null) + { + if (oldPositionX == 0.0f && oldPositionY == 0.0f && oldPositionZ == 0.0f) + { + oldPositionX = positionX; + oldPositionY = positionY; + oldPositionZ = positionZ; + } + + // todo: something went wrong + if (path.Count == 0) + { + positionX = oldPositionX; + positionY = oldPositionY; + positionZ = oldPositionZ; + } + + positionUpdates = path; + + this.hasMoved = true; + this.isAtSpawn = false; + + sw.Stop(); + ((Zone)zone).pathCalls++; + ((Zone)zone).pathCallTime += sw.ElapsedMilliseconds; + Program.Log.Error("[{0}][{1}] Created {2} points in {3} milliseconds", actorId, actorName, path.Count, sw.ElapsedMilliseconds); + } + } + + public void FollowTarget(Actor target, float stepSize = 1.2f, int maxPath = 25) + { + var player = target as Player; + + if (player != null) + { + if (this.target != player) + { + #region super important performance critical code + var chatMode = Program.Random.Next(13); + var emphasis = Program.Random.Next(9); + var drag = Program.Random.Next(7); + + chatMode = chatMode.Clamp(1, 12); + + string oni = "ONI"; + string chan = "CHA"; + + for (var i = 0; i < emphasis; ++i) + oni += "I"; + + for (var i = 0; i < drag; ++i) + chan += "A"; + + oni += "-"; + chan += "N"; + + // imouto aggro + player.SendMessage((uint)chatMode, "Rowena", oni + chan); + // sing for onii + this.PlayAnimation(Program.Random.Next(0,2) == 1 ? (uint)67111904 : (uint)67108902); + + #endregion + + this.target = target; + } + this.moveState = player.moveState; + this.moveSpeeds = player.moveSpeeds; + + PathTo(player.positionX, player.positionY, player.positionZ, stepSize, maxPath); + } + } + + public void OnPath() + { + // todo: lua function onPath in mob script + } + + public void Update(double deltaTime) + { + var diffTime = (DateTime.Now - lastAiUpdate); + + if (this is Player) + { + // todo: handle player stuff here + } + else + { + // todo: handle mobs only? + //if (this.isStatic) + // return; + + // todo: this too + if (diffTime.Milliseconds >= deltaTime) + { + bool foundActor = false; + bool despawnOutOfRange = false; + + var targId = target != null ? actorId : 0; + + // leash back to spawn + if (!isMovingToSpawn && this.oldPositionX != 0.0f && this.oldPositionY != 0.0f && this.oldPositionZ != 0.0f) + { + var spawnDistance = Utils.Distance(positionX, positionY, positionZ, oldPositionX, oldPositionY, oldPositionZ); + + // despawn if too far from spawn so client can reload me + if (spawnDistance >= 63) + { + despawnOutOfRange = true; + + if (target != null) + { + var player = target as Player; + + // target zoned, deaggro + target = null; + + + // tell player to despawn us and we can move back to spawn + if (player != null) + { + // make sure we dont tell player to despawn us twice + targId = player.actorId; + player.QueuePacket(RemoveActorPacket.BuildPacket(player.actorId, actorId)); + } + } + this.isMovingToSpawn = true; + this.positionUpdates.Clear(); + this.lastMoveUpdate = this.lastMoveUpdate.AddSeconds(-5); + } + // set a leash to path back to spawn even if have target + else if (spawnDistance >= 55) + { + this.isMovingToSpawn = true; + this.target = null; + this.positionUpdates.Clear(); + this.lastMoveUpdate = this.lastMoveUpdate.AddSeconds(-5); + } + } + + foreach (var actor in zone.GetActorsAroundActor(this, 65)) + { + if (actor is Player && actor != this) + { + var player = actor as Player; + + // dont despawn again if we already told target to despawn us + if (despawnOutOfRange && player.actorId != targId) + { + player.QueuePacket(RemoveActorPacket.BuildPacket(player.actorId, this.actorId)); + continue; + } + + // find distance between self and target + var distance = Utils.Distance(positionX, positionY, positionZ, player.positionX, player.positionY, player.positionZ); + + int maxDistance = player == target ? 27 : 15; + + // check target isnt too far + if (distance <= maxDistance) + { + foundActor = true; + + if (!hasMoved) + { + if (distance >= 3) + { + FollowTarget(player, 2.0f); + } + // too close, spread out + else if (distance <= 0.64f) + { + var minRadius = 0.65f; + var maxRadius = 0.85f; + + var angle = Program.Random.NextDouble() * Math.PI * 2; + var radius = Math.Sqrt(Program.Random.NextDouble() * (maxRadius - minRadius)) + minRadius; + + float x = (float)(radius * Math.Cos(angle)); + float z = (float)(radius * Math.Sin(angle)); + + positionUpdates.Add(new utils.Vector3(positionX + x, positionY, positionZ + z)); + + hasMoved = true; + } + + if (target != null) + { + LookAt(target); + } + } + } + break; + } + } + var diffMove = (DateTime.Now - lastMoveUpdate); + + // player disappeared + if (diffMove.Seconds >= 5 && !foundActor) + { + // dont path if havent moved before + if (oldPositionX != 0.0f && oldPositionY != 0.0f && oldPositionZ != 0.0f) + { + // check within spawn radius + this.isAtSpawn = Utils.Distance(positionX, positionY, positionZ, oldPositionX, oldPositionY, oldPositionZ) <= 25.0f; + + // make sure we have no target + if (this.target == null) + { + // path back to spawn + if (!this.isAtSpawn) + { + PathTo(oldPositionX, oldPositionY, oldPositionZ, 2.8f); + } + // within spawn range, find a random point + else if (diffMove.Seconds >= 15 && !hasMoved) + { + // this shit gets hit every time, but it wont path to it? + Program.Log.Error("{0} Picking random point to walk to!", actorId); + PathTo(oldPositionX, oldPositionY, oldPositionZ, 2.5f, 15, 20.5f); + + // face destination + if (positionUpdates.Count > 0) + { + var destinationPos = positionUpdates[positionUpdates.Count - 1]; + LookAt(destinationPos.X, destinationPos.Y); + } + this.isMovingToSpawn = false; + } + // already at spawn, dont recalculate distance on next ai update + else + { + this.isMovingToSpawn = false; + } + } + } + // todo: this is retarded. actually no it isnt, i didnt deaggro if out of range.. + target = null; + } + lastAiUpdate = DateTime.Now; + } + } + } } } diff --git a/FFXIVClassic Map Server/dataobjects/Session.cs b/FFXIVClassic Map Server/dataobjects/Session.cs index 6b5814d4..ccc83251 100644 --- a/FFXIVClassic Map Server/dataobjects/Session.cs +++ b/FFXIVClassic Map Server/dataobjects/Session.cs @@ -134,7 +134,7 @@ namespace FFXIVClassic_Map_Server.dataobjects { //Don't send for static characters (npcs) // todo: this is retarded, need actual mob class - if (actor is Character && !actor.hasMoved && ((Character)actor).isStatic) + if (actor is Character && !actor.hasMoved) continue; // todo: again, this is retarded but debug stuff diff --git a/FFXIVClassic Map Server/navmesh/SharpNav.dll b/FFXIVClassic Map Server/navmesh/SharpNav.dll new file mode 100644 index 00000000..3c648e5c Binary files /dev/null and b/FFXIVClassic Map Server/navmesh/SharpNav.dll differ diff --git a/FFXIVClassic Map Server/navmesh/rcdtcs b/FFXIVClassic Map Server/navmesh/rcdtcs deleted file mode 160000 index 8eea2772..00000000 --- a/FFXIVClassic Map Server/navmesh/rcdtcs +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 8eea27727df5132c5f0e6732d1bd36238ce300ea diff --git a/FFXIVClassic Map Server/utils/NavmeshUtils.cs b/FFXIVClassic Map Server/utils/NavmeshUtils.cs index 2757db3c..1057132d 100644 --- a/FFXIVClassic Map Server/utils/NavmeshUtils.cs +++ b/FFXIVClassic Map Server/utils/NavmeshUtils.cs @@ -94,15 +94,6 @@ namespace FFXIVClassic_Map_Server.utils return false; } - public static Detour.dtNavMesh LoadNavmesh(string path) - { - var bytes = File.ReadAllBytes(path); - var start = 0; - - var navmesh = Detour.NavMeshSetBuild.FromBytes(bytes, ref start); - return navmesh; - } - public static SharpNav.TiledNavMesh LoadNavmesh(TiledNavMesh navmesh, string filePath) { var serialiser = new SharpNav.IO.Json.NavMeshJsonSerializer(); @@ -110,14 +101,6 @@ namespace FFXIVClassic_Map_Server.utils //return navmesh = new SharpNav.IO.Json.NavMeshJsonSerializer().Deserialize(filePath); } - public static List GetPath(Detour.dtNavMesh navmesh, Vector3 start, Vector3 end) - { - var path = new Vector3[] { }; - - - return path.ToList(); - } - #region sharpnav stuff // Copyright (c) 2013-2016 Robert Rouhani and other contributors (see CONTRIBUTORS file). // Licensed under the MIT License - https://raw.github.com/Robmaister/SharpNav/master/LICENSE @@ -127,16 +110,19 @@ namespace FFXIVClassic_Map_Server.utils var navMesh = zone.tiledNavMesh; var navMeshQuery = zone.navMeshQuery; - if (navMesh == null || (startVec.X == endVec.X && startVec.Y == endVec.Y && startVec.Z == endVec.Z)) + if (navMesh == null || (startVec.X == endVec.X && startVec.Y == endVec.Y && startVec.Z == endVec.Z && polyRadius == 0.0f)) { + Program.Log.Error("ass"); return null; } - float distanceSquared = FFXIVClassic.Common.Utils.DistanceSquared(startVec.X, startVec.Y, startVec.Z, endVec.X, endVec.Y, endVec.Z); + // we dont care about distance if picking random point + float distanceSquared = polyRadius == 0.0f ? FFXIVClassic.Common.Utils.DistanceSquared(startVec.X, startVec.Y, startVec.Z, endVec.X, endVec.Y, endVec.Z) : 100; // no point pathing if in range if (distanceSquared < 4 && Math.Abs(startVec.Y - endVec.Y) < 1.1f) { + Program.Log.Error("shit"); return null; } @@ -145,8 +131,6 @@ namespace FFXIVClassic_Map_Server.utils NavQueryFilter filter = new NavQueryFilter(); NavPoint startPt, endPt; - RaycastHit hit; - PathCorridor corridor = new PathCorridor(); try { @@ -172,6 +156,9 @@ namespace FFXIVClassic_Map_Server.utils smoothPath.Add(new Vector3(iterPos)); + if (npolys <= 1) + System.Diagnostics.Debugger.Break(); + //float STEP_SIZE = 0.70f; float SLOP = 0.15f; while (npolys > 0 && smoothPath.Count < smoothPath.Capacity) @@ -214,7 +201,7 @@ namespace FFXIVClassic_Map_Server.utils iterPos = result; //handle end of path when close enough - if (endOfPath && InRange(iterPos, steerPos, SLOP, 1000.0f)) + if (endOfPath && InRange(iterPos, steerPos, SLOP, 10.0f)) { //reached end of path iterPos = targetPos; diff --git a/FFXIVClassic.sln b/FFXIVClassic.sln index 4a0fe45b..bbc9a950 100644 --- a/FFXIVClassic.sln +++ b/FFXIVClassic.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 14 -VisualStudioVersion = 14.0.23107.0 +# Visual Studio 15 +VisualStudioVersion = 15.0.26430.6 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FFXIVClassic Map Server", "FFXIVClassic Map Server\FFXIVClassic Map Server.csproj", "{E8FA2784-D4B9-4711-8CC6-712A4B1CD54F}" ProjectSection(ProjectDependencies) = postProject @@ -52,4 +52,7 @@ Global GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(Performance) = preSolution + HasPerformanceSessions = true + EndGlobalSection EndGlobal