/// <summary> /// Expects operation EnterWorld and creates a new MmoActor with a new Item as avatar and a new MmoClientInterestArea. /// </summary> /// <remarks> /// The MmoActor becomes the new Peer.CurrentOperationHandler. /// If another MmoActor with the same name exists he is disconnected. /// An OperationResponse with error code ReturnCode.Ok is published on success. /// </remarks> public OperationResponse OperationEnterWorld(PeerBase peer, OperationRequest request, SendParameters sendParameters) { var operation = new EnterWorld(peer.Protocol, request); if (!operation.IsValid) { return new OperationResponse(request.OperationCode) { ReturnCode = (int)ReturnCode.InvalidOperationParameter, DebugMessage = operation.GetErrorMessage() }; } World world; if (WorldCache.Instance.TryGet(operation.WorldName, out world) == false) { return operation.GetOperationResponse((int)ReturnCode.WorldNotFound, "WorldNotFound"); } var interestArea = new ClientInterestArea(peer, operation.InterestAreaId, world) { ViewDistanceEnter = operation.ViewDistanceEnter, ViewDistanceExit = operation.ViewDistanceExit }; var actor = new MmoActorOperationHandler(peer, world, interestArea); var avatar = new Item(operation.Position, operation.Rotation, operation.Properties, actor, operation.Username, (byte)ItemType.Avatar, world); while (world.ItemCache.AddItem(avatar) == false) { Item otherAvatarItem; if (world.ItemCache.TryGetItem(avatar.Id, out otherAvatarItem)) { avatar.Dispose(); actor.Dispose(); interestArea.Dispose(); (((Item)otherAvatarItem).Owner).DisconnectByOtherPeer(this.peer, request, sendParameters); // request continued later, no response here return null; } } // init avatar actor.AddItem(avatar); actor.Avatar = avatar; ((Peer)peer).SetCurrentOperationHandler(actor); // set return values var responseObject = new EnterWorldResponse { BoundingBox = world.Area, TileDimensions = world.TileDimensions, WorldName = world.Name }; // send response; use item channel to ensure that this event arrives before any move or subscribe events var response = new OperationResponse(request.OperationCode, responseObject); sendParameters.ChannelId = Settings.ItemEventChannel; peer.SendOperationResponse(response, sendParameters); lock (interestArea.SyncRoot) { interestArea.AttachToItem(avatar); interestArea.UpdateInterestManagement(); } avatar.Spawn(operation.Position); world.Radar.AddItem(avatar, operation.Position); // response already sent return null; }
/// <summary> /// Handles operation AddInterestArea: Creates a new InterestArea and optionally attaches it to an existing Item. /// </summary> public OperationResponse OperationAddInterestArea(PeerBase peer, OperationRequest request, SendParameters sendParameters) { var operation = new AddInterestArea(peer.Protocol, request); if (!operation.IsValid) { return new OperationResponse(request.OperationCode) { ReturnCode = (int)ReturnCode.InvalidOperationParameter, DebugMessage = operation.GetErrorMessage() }; } operation.OnStart(); InterestArea interestArea; if (this.TryGetInterestArea(operation.InterestAreaId, out interestArea)) { return operation.GetOperationResponse((int)ReturnCode.InterestAreaAlreadyExists, "InterestAreaAlreadyExists"); } interestArea = new ClientInterestArea(this.Peer, operation.InterestAreaId, this.World); this.AddInterestArea(interestArea); // attach interestArea to item if (string.IsNullOrEmpty(operation.ItemId) == false) { Item item; bool actorItem = this.TryGetItem(operation.ItemId, out item); if (actorItem) { // we are already in the item thread, invoke directly return ItemOperationAddInterestArea(item, operation, interestArea); } else { if (this.World.ItemCache.TryGetItem(operation.ItemId, out item) == false) { return operation.GetOperationResponse((int)ReturnCode.ItemNotFound, "ItemNotFound"); } else { // second parameter (peer) allows us to send an error event to the client (in case of an error) item.Fiber.Enqueue(() => this.ExecItemOperation(() => ItemOperationAddInterestArea(item, operation, interestArea), sendParameters)); // send response later return null; } } } else { // free floating interestArea lock (interestArea.SyncRoot) { interestArea.Position = operation.Position; interestArea.ViewDistanceEnter = operation.ViewDistanceEnter; interestArea.ViewDistanceExit = operation.ViewDistanceExit; interestArea.UpdateInterestManagement(); } return operation.GetOperationResponse(MethodReturnValue.Ok); } }
/// <summary> /// Handles operation AddInterestArea: Creates a new InterestArea and optionally attaches it to an existing Item. /// </summary> public OperationResponse OperationAddInterestArea(PeerBase peer, OperationRequest request, SendParameters sendParameters) { var operation = new AddInterestArea(peer.Protocol, request); if (!operation.IsValid) { return(new OperationResponse(request.OperationCode) { ReturnCode = (int)ReturnCode.InvalidOperationParameter, DebugMessage = operation.GetErrorMessage() }); } operation.OnStart(); InterestArea interestArea; if (this.TryGetInterestArea(operation.InterestAreaId, out interestArea)) { return(operation.GetOperationResponse((int)ReturnCode.InterestAreaAlreadyExists, "InterestAreaAlreadyExists")); } interestArea = new ClientInterestArea(this.Peer, operation.InterestAreaId, this.World); this.AddInterestArea(interestArea); // attach interestArea to item if (string.IsNullOrEmpty(operation.ItemId) == false) { Item item; bool actorItem = this.TryGetItem(operation.ItemId, out item); if (actorItem) { // we are already in the item thread, invoke directly return(ItemOperationAddInterestArea(item, operation, interestArea)); } else { if (this.World.ItemCache.TryGetItem(operation.ItemId, out item) == false) { return(operation.GetOperationResponse((int)ReturnCode.ItemNotFound, "ItemNotFound")); } else { // second parameter (peer) allows us to send an error event to the client (in case of an error) item.Fiber.Enqueue(() => this.ExecItemOperation(() => ItemOperationAddInterestArea(item, operation, interestArea), sendParameters)); // send response later return(null); } } } else { // free floating interestArea lock (interestArea.SyncRoot) { interestArea.Position = operation.Position; interestArea.ViewDistanceEnter = operation.ViewDistanceEnter; interestArea.ViewDistanceExit = operation.ViewDistanceExit; interestArea.UpdateInterestManagement(); } return(operation.GetOperationResponse(MethodReturnValue.Ok)); } }
/// <summary> /// Expects operation EnterWorld and creates a new MmoActor with a new Item as avatar and a new MmoClientInterestArea. /// </summary> /// <remarks> /// The MmoActor becomes the new Peer.CurrentOperationHandler. /// If another MmoActor with the same name exists he is disconnected. /// An OperationResponse with error code ReturnCode.Ok is published on success. /// </remarks> public OperationResponse OperationEnterWorld(PeerBase peer, OperationRequest request, SendParameters sendParameters) { var operation = new EnterWorld(peer.Protocol, request); if (!operation.IsValid) { return(new OperationResponse(request.OperationCode) { ReturnCode = (int)ReturnCode.InvalidOperationParameter, DebugMessage = operation.GetErrorMessage() }); } World world; if (WorldCache.Instance.TryGet(operation.WorldName, out world) == false) { return(operation.GetOperationResponse((int)ReturnCode.WorldNotFound, "WorldNotFound")); } var interestArea = new ClientInterestArea(peer, operation.InterestAreaId, world) { ViewDistanceEnter = operation.ViewDistanceEnter, ViewDistanceExit = operation.ViewDistanceExit }; var actor = new MmoActorOperationHandler(peer, world, interestArea); var avatar = new Item(operation.Position, operation.Rotation, operation.Properties, actor, operation.Username, (byte)ItemType.Avatar, world); while (world.ItemCache.AddItem(avatar) == false) { Item otherAvatarItem; if (world.ItemCache.TryGetItem(avatar.Id, out otherAvatarItem)) { avatar.Dispose(); actor.Dispose(); interestArea.Dispose(); (((Item)otherAvatarItem).Owner).DisconnectByOtherPeer(this.peer, request, sendParameters); // request continued later, no response here return(null); } } // init avatar actor.AddItem(avatar); actor.Avatar = avatar; ((Peer)peer).SetCurrentOperationHandler(actor); // set return values var responseObject = new EnterWorldResponse { BoundingBox = world.Area, TileDimensions = world.TileDimensions, WorldName = world.Name }; // send response; use item channel to ensure that this event arrives before any move or subscribe events var response = new OperationResponse(request.OperationCode, responseObject); sendParameters.ChannelId = Settings.ItemEventChannel; peer.SendOperationResponse(response, sendParameters); lock (interestArea.SyncRoot) { interestArea.AttachToItem(avatar); interestArea.UpdateInterestManagement(); } avatar.Spawn(operation.Position); world.Radar.AddItem(avatar, operation.Position); // response already sent return(null); }