public void RemoveWorldObject(ObjectGuid objectId, bool adjacencyMove) { WorldObject wo = null; Log($"removing {objectId.Full.ToString("X")}"); lock (objectCacheLocker) { if (worldObjects.ContainsKey(objectId)) { wo = worldObjects[objectId]; if (!objectId.IsCreature()) { worldObjects.Remove(objectId); } } } // suppress broadcasting when it's just an adjacency move. clients will naturally just stop // tracking stuff if they're too far, or the new landblock will broadcast to them if they're // close enough. // if we are in a container - we need never broadcast a destroy - just remove from landblock above. if (!adjacencyMove && id.MapScope == Enum.MapScope.Outdoors && wo != null) { var args = BroadcastEventArgs.CreateAction(BroadcastAction.Delete, wo); Broadcast(args, true, Quadrant.All); } }
public void AddWorldObject(WorldObject wo) { List <WorldObject> allObjects; Log($"adding {wo.Guid.Full.ToString("X")}"); lock (objectCacheLocker) { allObjects = worldObjects.Values.ToList(); if (!worldObjects.ContainsKey(wo.Guid)) { worldObjects[wo.Guid] = wo; } } var args = BroadcastEventArgs.CreateAction(BroadcastAction.AddOrUpdate, wo); Broadcast(args, true, Quadrant.All); // if this is a player, tell them about everything else we have in range of them. if (wo is Player) { List <WorldObject> wolist = null; wolist = GetWorldObjectsInRange(wo, maxobjectRange, true); AddPlayerTracking(wolist, (wo as Player)); } }
public void AddWorldObject(WorldObject wo) { List <WorldObject> allObjects; Log($"adding {wo.Guid.Full.ToString("X")}"); lock (objectCacheLocker) { allObjects = worldObjects.Values.ToList(); if (!worldObjects.ContainsKey(wo.Guid)) { worldObjects[wo.Guid] = wo; } } var args = BroadcastEventArgs.CreateAction(BroadcastAction.AddOrUpdate, wo); Broadcast(args, true, Quadrant.All); // if this is a player, tell them about everything else we have. if (wo is Player) { // send them the initial burst of objects Log($"blasting player \"{(wo as Player).Name}\" with {allObjects.Count} objects."); Parallel.ForEach(allObjects, (o) => { if (o.Guid.IsCreature()) { if ((o as Creature).IsAlive) { (wo as Player).TrackObject(o); } } else { (wo as Player).TrackObject(o); } }); } }
/// <summary> /// main game loop /// </summary> public void UseTime() { while (running) { // here we'd move server objects in motion (subject to landscape) and do physics collision detection List <WorldObject> allworldobj = null; List <Player> allplayers = null; List <Creature> allcreatures = null; List <WorldObject> movedObjects = null; List <WorldObject> despawnObjects = null; List <Creature> deadCreatures = null; lock (objectCacheLocker) { allworldobj = worldObjects.Values.ToList(); } // all players on this land block allplayers = allworldobj.OfType <Player>().ToList(); allcreatures = allworldobj.OfType <Creature>().ToList(); despawnObjects = allworldobj.ToList(); despawnObjects = despawnObjects.Where(x => x.DespawnTime > -1).ToList(); deadCreatures = allworldobj.OfType <Creature>().ToList(); deadCreatures = deadCreatures.Where(x => x.IsAlive == false).ToList(); // flag them as updated now in order to reduce chance of missing an update // this is only for moving objects across landblocks. movedObjects = allworldobj.ToList(); movedObjects = movedObjects.Where(p => p.LastUpdatedTicks >= p.LastMovementBroadcastTicks).ToList(); movedObjects.ForEach(m => m.LastMovementBroadcastTicks = WorldManager.PortalYearTicks); if (id.MapScope == Enum.MapScope.Outdoors) { // check to see if a player or other mutable object "roamed" to an adjacent landblock var objectsToRelocate = movedObjects.Where(m => m.Location.LandblockId.IsAdjacentTo(id) && m.Location.LandblockId != id).ToList(); // so, these objects moved to an adjacent block. they could have recalled to that block, died and bounced to a lifestone on that block, or // just simply walked accross the border line. in any case, we won't delete them, we'll just transfer them. the trick, though, is to // figure out how to treat it in adjacent landblocks. if the player walks across the southern border, the north adjacency needs to remove // them, but the south is actually getting them. we need to avoid sending Delete+Create to clients that already know about it, though. objectsToRelocate.ForEach(o => Log($"attempting to relocate object {o.Name} ({o.Guid.Full.ToString("X")})")); // RelocateObject will put them in the right landblock objectsToRelocate.ForEach(o => LandblockManager.RelocateObject(o)); // Remove has logic to make sure it doesn't double up the delete+create when "true" is passed. objectsToRelocate.ForEach(o => RemoveWorldObject(o.Guid, true)); } // for all players on landblock. Parallel.ForEach(allplayers, player => { // Process Action Queue for player. QueuedGameAction action = player.ActionQueuePop(); if (action != null) { HandleGameAction(action, player); } // Process Examination Queue for player QueuedGameAction examination = player.ExaminationQueuePop(); if (examination != null) { HandleGameAction(examination, player); } }); UpdateStatus(allplayers.Count); double tickTime = WorldManager.PortalYearTicks; // per-creature update on landblock. Parallel.ForEach(allworldobj, wo => { // Process the creatures wo.Tick(tickTime); }); // broadcast moving objects to the world.. // players and creatures can move. Parallel.ForEach(movedObjects, mo => { // detect all world objects in ghost range List <WorldObject> woproxghost = new List <WorldObject>(); woproxghost.AddRange(GetWorldObjectsInRange(mo, maxobjectGhostRange, true)); // for all objects in rang of this moving object or in ghost range of moving object update them. Parallel.ForEach(woproxghost, wo => { if (mo.Guid.IsPlayer()) { // if world object is in active zone then. if (wo.Location.SquaredDistanceTo(mo.Location) <= maxobjectRange) { // if world object is in active zone. if (!(mo as Player).GetTrackedObjectGuids().Contains(wo.Guid)) { (mo as Player).TrackObject(wo); } } // if world object is in ghost zone and outside of active zone else if ((mo as Player).GetTrackedObjectGuids().Contains(wo.Guid)) { (mo as Player).StopTrackingObject(wo, true); } } }); if (mo.Location.LandblockId == id) { // update if it's still here Broadcast(BroadcastEventArgs.CreateAction(BroadcastAction.AddOrUpdate, mo), true, Quadrant.All); } else { // remove and readd if it's not RemoveWorldObject(mo.Guid, false); LandblockManager.AddObject(mo); } }); // despawn objects despawnObjects.ForEach(deo => { if (deo.DespawnTime < WorldManager.PortalYearTicks) { RemoveWorldObject(deo.Guid, false); } }); // respawn creatures deadCreatures.ForEach(dc => { if (dc.RespawnTime < WorldManager.PortalYearTicks) { dc.IsAlive = true; // HandleParticleEffectEvent(dc, PlayScript.Create); AddWorldObject(dc); } }); Thread.Sleep(1); } // TODO: release resources }
/// <summary> /// main game loop /// </summary> public void UseTime() { while (running) { // here we'd move server objects in motion (subject to landscape) and do physics collision detection // for now, we'll move players around List <WorldObject> movedObjects = null; List <Player> players = null; lock (objectCacheLocker) { movedObjects = this.worldObjects.Values.OfType <WorldObject>().ToList(); players = this.worldObjects.Values.OfType <Player>().ToList(); } movedObjects = movedObjects.Where(p => p.LastUpdatedTicks >= p.LastMovementBroadcastTicks).ToList(); // flag them as updated now in order to reduce chance of missing an update movedObjects.ForEach(m => m.LastMovementBroadcastTicks = WorldManager.PortalYearTicks); if (this.id.MapScope == Enum.MapScope.Outdoors) { // check to see if a player or other mutable object "roamed" to an adjacent landblock var objectsToRelocate = movedObjects.Where(m => m.Location.LandblockId.IsAdjacentTo(this.id) && m.Location.LandblockId != this.id).ToList(); // so, these objects moved to an adjacent block. they could have recalled to that block, died and bounced to a lifestone on that block, or // just simply walked accross the border line. in any case, we won't delete them, we'll just transfer them. the trick, though, is to // figure out how to treat it in adjacent landblocks. if the player walks across the southern border, the north adjacency needs to remove // them, but the south is actually getting them. we need to avoid sending Delete+Create to clients that already know about it, though. objectsToRelocate.ForEach(o => Log($"attempting to relocate object {o.Name} ({o.Guid.Full.ToString("X")})")); // RelocateObject will put them in the right landblock objectsToRelocate.ForEach(o => LandblockManager.RelocateObject(o)); // Remove has logic to make sure it doesn't double up the delete+create when "true" is passed. objectsToRelocate.ForEach(o => RemoveWorldObject(o.Guid, true)); } // broadcast Parallel.ForEach(movedObjects, mo => { if (mo.Location.LandblockId == this.id) { // update if it's still here Broadcast(BroadcastEventArgs.CreateAction(BroadcastAction.AddOrUpdate, mo), true, Quadrant.All); } else { // remove and readd if it's not this.RemoveWorldObject(mo.Guid, false); LandblockManager.AddObject(mo); } }); // TODO: figure out if this landblock can be unloaded // process player action queues foreach (Player p in players) { QueuedGameAction action = p.ActionQueuePop(); if (action != null) { HandleGameAction(action, p); } } Thread.Sleep(1); } // TODO: release resources }