public async Task GetP2PData(RequestContext <IScenePeerClient> ctx) { var provider = ctx.ReadObject <string>(); switch (provider) { case "userId": { var userId = ctx.ReadObject <string>(); var peer = await _userSessions.GetPeer(userId); if (peer == null) { throw new ClientException($"The user '{userId}' is not connected."); } var r = await _natIndex.TryGet(peer.Id.ToString()); if (!r.Success) { throw new ClientException($"The user '{userId}' (peer : '{peer.Id}') is not available for p2p (no p2p data set)."); } else { ctx.SendValue(r.Value.P2PTransports); } break; } case "peerId": { var peerId = ctx.ReadObject <long>(); var r = await _natIndex.TryGet(peerId.ToString()); if (!r.Success) { ctx.SendValue(new Dictionary <string, string>()); } else { ctx.SendValue(r.Value.P2PTransports); } break; } default: throw new ClientException($"Unknown provider '{provider}'."); } }
public async Task Report(RequestContext <IScenePeerClient> ctx) { try { var user = await _userSessions.GetUser(ctx.RemotePeer); var reportDto = ctx.ReadObject <ReportDto>(); var report = new Report { ReportUserId = user.Id, ReportedUserId = reportDto.ReportedUserId, ReportDate = DateTimeOffset.FromUnixTimeSeconds(reportDto.Timestamp).DateTime, Message = reportDto.Message, Category = reportDto.Category, CustomData = reportDto.CustomData, }; await _reportPlayers.ReportPlayer(report); } catch (Exception ex) { _logger.Log(LogLevel.Error, _logCategory, "Server error, report doesn't save", ex.Message); throw new ClientException("Server error, report doesn't save"); } ctx.SendValue <string>("Report saved"); }
public Task GetPlayerData(RequestContext <IScenePeerClient> ctx) { var playerId = ctx.ReadObject <string>(); ctx.SendValue(_service.GetPlayerData(playerId)); return(Task.CompletedTask); }
public async Task Message(RequestContext <IScenePeerClient> ctx) { try { await _chat.OnMessageReceived(ctx.RemotePeer, ctx.ReadObject <string>(), (messageDto, destination) => { if (messageDto == null) { throw new InvalidOperationException("Message recieved internal error"); } if (destination.HasFlag(DestinationType.Others)) { _scene.Broadcast("receivemessage", messageDto); } if (destination.HasFlag(DestinationType.Self)) { ctx.SendValue(messageDto); } }); } catch (Exception ex) { _logger.Log(LogLevel.Error, _logCategory, "Error occured when server received message", ex); throw new ClientException(ex.Message); } }
public async Task Cursor(RequestContext <IScenePeerClient> ctx) { var cursor = ctx.ReadObject <string>(); var result = await _leaderboard.QueryCursor(cursor); ctx.SendValue(result); }
public async Task GetShard(RequestContext <IScenePeerClient> ctx) { var client = await _management.GetApplicationClient(); // Get data send by client var mapId = ctx.ReadObject <string>(); var shardId = StringToHex(mapId); var shard = await client.GetScene(shardId); if (shard == null) { var metadata = Newtonsoft.Json.Linq.JObject.FromObject(new { gameSession = new Server.Plugins.GameSession.GameSessionConfiguration { Public = true, canRestart = true, UserData = mapId } }); var template = global::Server.App.GAMESESSION_TEMPLATE; _logger.Log(LogLevel.Trace, "LocatorController", $"Creating scene {shardId} for map {mapId}", new { mapId, shardId }); await client.CreateScene( shardId, template, false, metadata, false); } var token = await client.CreateConnectionToken(shardId, ""); ctx.SendValue(token); }
public Task <bool> ExtractData(string provider, RequestContext <IScenePeerClient> request, Group group) { //logger.Log(LogLevel.Info, "gamefinder", "Received player", new { group = group }); var parameters = request.ReadObject <GameFinderParameters>(); group.GroupData.Options = parameters; return(Task.FromResult(true)); }
public Task Push(RequestContext <IScenePeerClient> ctx) { var docs = ctx.ReadObject <List <Document> >(); foreach (var doc in docs) { _analytics.Push(doc.Type, doc.Content); } return(Task.FromResult(true)); }
public async Task Submit(RequestContext <IScenePeerClient> ctx) { var userId = (await _sessions.GetUser(ctx.RemotePeer))?.Id; if (userId == null) { throw new ClientException("Authentication required"); } var dto = ctx.ReadObject <TransactionDto>(); await _game.SubmitTransaction(userId, dto.PlayerId, dto.Command, JObject.Parse(dto.Args)); }
public async Task GetUserFromBearerToken(RequestContext <IScenePeerClient> ctx) { var app = await _environment.GetApplicationInfos(); var data = TokenGenerator.DecodeToken <BearerTokenData>(ctx.ReadObject <string>(), app.PrimaryKey); if (data == null) { throw new ClientException("Invalid Token"); } ctx.SendValue(data.UserId); }
public async Task UpdateP2PData(RequestContext <IScenePeerClient> ctx) { var user = await _userSessions.GetUser(ctx.RemotePeer); if (user == null) { //_logger.Trace("natTraversal", $"Failed to update nat traversal data: the peer '{ctx.RemotePeer.Id}' is not logged in."); throw new ClientException($"The peer '{ctx.RemotePeer.Id}' is not logged in."); } var p2p = ctx.ReadObject <Dictionary <string, string> >(); var r = await _natIndex.AddOrUpdateWithRetries(ctx.RemotePeer.Id.ToString(), new NatPunchthroughData { P2PTransports = p2p }, d => d.SetP2P(p2p)); }
public async Task SetPlayerData(RequestContext <IScenePeerClient> ctx) { var player = await _sessions.GetUser(ctx.RemotePeer); var data = ctx.ReadObject <MessagePackObject>(); if (player == null) { throw new ClientException("Player not authenticated."); } _service.SetPlayerData(player.Id, data); }
public async Task Query(RequestContext <IScenePeerClient> ctx) { var rq = ctx.ReadObject <LeaderboardQuery>(); if (rq.Count <= 0) { rq.Count = 10; } var r = await _leaderboard.Query(rq); ctx.SendValue(r); }
public async Task AddPlayer(RequestContext <IScenePeerClient> ctx) { var userId = (await _sessions.GetUser(ctx.RemotePeer))?.Id; if (userId == null) { throw new ClientException("Authentication required"); } var dto = ctx.ReadObject <string>(); _game.AddPlayer(userId, dto); }
public async Task UpdateShutdownMode(RequestContext <IScenePeerClient> ctx) { ShutdownModeParameters shutdown = ctx.ReadObject <ShutdownModeParameters>(); if (_service.IsHost(ctx.RemotePeer.SessionId)) { await _service.UpdateShutdownMode(shutdown); } else { throw new ClientException("forbidden"); } }
public async Task GetToken(RequestContext <IScenePeerClient> ctx) { var client = await _accessor.GetApplicationClient(); var user = await _sessions.GetUser(ctx.RemotePeer); var sceneId = ctx.ReadObject <string>(); _logger.Log(LogLevel.Debug, "authorization", $"Authorizing access to scene '{sceneId}'", new { sceneId, user.Id }); var token = await client.CreateConnectionToken(sceneId, new byte[0], "application/octet-stream"); ctx.SendValue(token); }
public async Task GetFile(RequestContext <IScenePeerClient> ctx) { var branchName = ctx.ReadObject <string>(); var path = ctx.ReadObject <string>(); MetafileDto metafileDto = null; try { metafileDto = await _assetsStorage.GetFile(branchName, path); } catch (BranchException ex) { _logger.Log(LogLevel.Warn, "AssetsStorageController", $"Branch not found: { branchName } ", ex.Message); throw new ClientException($"Branch Not Found:{ branchName }"); } catch (FileException ex) { _logger.Log(LogLevel.Warn, "AssetsStorageController", $"File not found: { path } ", ex.Message); throw new ClientException($"File not found :{ path }"); } ctx.SendValue(metafileDto); }
public async Task LoadHistory(RequestContext <IScenePeerClient> ctx) { List <ChatMessageDto> result = new List <ChatMessageDto>(); try { long startTimestamp = ctx.ReadObject <long>(); long endTimestamp = ctx.ReadObject <long>(); DateTime start = TimestampHelper.UnixTimeStampSecondToDateTime(startTimestamp); DateTime end = TimestampHelper.UnixTimeStampSecondToDateTime(endTimestamp); string channelName = ctx.RemotePeer.SceneId; var messagesData = await _chat.LoadHistory(channelName, start, end); foreach (ChatMessage msg in messagesData) { var message = new ChatMessageDto { Message = msg.Message, TimeStamp = TimestampHelper.DateTimeToUnixTimeStamp(msg.Date), UserInfo = new ChatUserInfoDto { UserId = msg.UserInfo.UserId, Data = msg.UserInfo.Data.ToString(), } }; result.Add(message); } } catch (Exception ex) { _logger.Log(LogLevel.Error, _logCategory, "Error occured when server try to load history", ex); throw new ClientException(ex.Message); } ctx.SendValue(result); }
public async Task GetToken(RequestContext <IScenePeerClient> ctx) { var client = await _accessor.GetApplicationClient(); var user = await _sessions.GetUser(ctx.RemotePeer); if (user == null) { throw new ClientException("userDisconnected"); } var sceneId = ctx.ReadObject <string>(); var token = await client.CreateConnectionToken(sceneId, new byte[0], "application/octet-stream"); await ctx.SendValue(token); }
private Task OnGetShipInfos(RequestContext<IScenePeerClient> arg) { var shipIds = arg.ReadObject<ushort[]>(); var ships = new List<ShipCreatedDto>(shipIds.Length); foreach (var id in shipIds) { Ship ship; if (_ships.TryGetValue(id, out ship)) { ships.Add(new ShipCreatedDto { timestamp = _scene.GetComponent<IEnvironment>().Clock, id = ship.id, team = ship.team, x = ship.x, y = ship.y, rot = ship.rot, weapons = ship.weapons, status = ship.Status }); } } arg.SendValue(ships); return Task.FromResult(true); }
public async Task GetToken(RequestContext <IScenePeerClient> ctx) { _logger.Log(LogLevel.Trace, "authorization", "Receiving a token request to access a scene", new { }); var client = await _accessor.GetApplicationClient(); var user = await _sessions.GetUser(ctx.RemotePeer); if (user == null) { throw new ClientException("Client is not logged in."); } var sceneId = ctx.ReadObject <string>(); _logger.Log(LogLevel.Debug, "authorization", $"Authorizing access to scene '{sceneId}'", new { sceneId, user.Id }); var token = await client.CreateConnectionToken(sceneId, new byte[0], "application/octet-stream"); ctx.SendValue(token); }
public async Task Promote(RequestContext <IScenePeerClient> ctx) { var currentUser = await GetUser(); var targetUserId = ctx.ReadObject <string>(); var group = await GetGroup(currentUser.Id); if (group.Leader != currentUser.Id) { throw new ClientException($"Only the leader can promote another player as leader."); } var r = await _groupsIndex.UpdateWithRetries(group.Id, g => g.PromoteAsLeader(targetUserId)); if (!r.Success) { throw new ClientException("Failed to promote player."); } await BroadCastGroupUpdate(r.Value); }
Task joinGameProcedure(RequestContext <IScenePeerClient> ctx) { // Make sure the client sends a unique name string name = ctx.ReadObject <string>(); foreach (KeyValuePair <long, Player> player in mPlayers) { if (player.Value.name == name) { throw new ClientException("The username you chose (\"" + name + "\") is already in use."); } } if (!mPlayers.TryAdd(ctx.RemotePeer.Id, new Player(name))) { throw new ClientException("The joinGame procedure must be called only once by clients."); } ctx.SendValue(mMapFilename); return(Task.FromResult(true)); }
public async Task RequestPasswordRecovery(RequestContext <IScenePeerClient> requestContext, ISceneHost scene) { var email = requestContext.ReadObject <string>(); var users = scene.DependencyResolver.Resolve <IUserService>(); var user = await users.GetUserByClaim(PROVIDER_NAME, "email", email); if (user == null) { return; } var auth = (JObject)user.Auth[PROVIDER_NAME]; var code = GenerateCode(); if (await scene.DependencyResolver.Resolve <Plugins.Notification.INotificationChannel>().SendNotification("loginpassword.resetRequest", JObject.FromObject(new { email, code }))) { auth["resetCode"] = JObject.FromObject(new { code = code, expiration = DateTime.UtcNow + TimeSpan.FromHours(1), retriesLeft = 3 }); await users.AddAuthentication(user, PROVIDER_NAME, auth, (string)user.Auth[PROVIDER_NAME]["login"]); } }
public async Task ChangePassword(RequestContext <IScenePeerClient> requestContext, ISceneHost scene) { var rq = requestContext.ReadObject <ChangePasswordRequest>(); var users = scene.DependencyResolver.Resolve <IUserService>(); var user = await users.GetUserByClaim(PROVIDER_NAME, "email", rq.Email); dynamic auth = user.Auth[PROVIDER_NAME]; var code = auth?.resetCode?.code as string; if (code == null) { throw new ClientException("Couldn't reset password. (Error 1)"); } var expiration = (DateTime)auth?.resetCode?.expiration; if (expiration < DateTime.UtcNow) { throw new ClientException("Couldn't reset password. (Error 3)"); } var retriesLeft = (int)auth?.resetCode?.retriesLeft; if (retriesLeft <= 0) { throw new ClientException("Couldn't reset passord. (Error 2)"); } if (code != rq.Code) { throw new ClientException("Couldn't reset password (Error 3)"); } var salt = GenerateSaltValue(); var hashedPassword = HashPassword(rq.NewPassword, salt); auth.salt = salt; auth.password = hashedPassword; await users.AddAuthentication(user, PROVIDER_NAME, auth, (string)auth.login); }
public async Task Send(RequestContext <IScenePeerClient> ctx) { var targetId = ctx.ReadObject <string>(); var target = await _sessions.GetPeer(targetId); var origin = await _sessions.GetUser(this.Request.RemotePeer); if (target == null) { throw new ClientException($"The user '{targetId}' is not connected"); } var routes = target.Routes.Select(r => r.Name).ToArray(); var memStream = new MemoryStream(); ctx.InputStream.CopyTo(memStream); memStream.Seek(0, SeekOrigin.Begin); target.Send("messaging.receive", s => { target.Serializer().Serialize(origin.Id, s); memStream.CopyTo(s); }, Core.PacketPriority.MEDIUM_PRIORITY, Core.PacketReliability.RELIABLE); }
public async Task GetUserFromBearerToken(RequestContext <IScenePeerClient> ctx) { var app = await _environment.GetApplicationInfos(); var bearerToken = ctx.ReadObject <string>(); if (string.IsNullOrEmpty(bearerToken)) { _logger.Log(LogLevel.Error, "authorization", $"Missing bearer token when calling GetUserFromBearerToken()", new { }); } var data = TokenGenerator.DecodeToken <BearerTokenData>(bearerToken, app.PrimaryKey); if (data == null) { throw new ClientException("Invalid Token"); } if (string.IsNullOrEmpty(data?.userId)) { _logger.Log(LogLevel.Error, "authorization", $"Failed to retrieve userId from bearer token data", new { data }); } await ctx.SendValue(data?.userId); }
public async Task GetSingle(RequestContext <IScenePeerClient> ctx) { var userId = ctx.RemotePeer.GetUserData <string>(); var profileId = ctx.ReadObject <string>(); if (string.IsNullOrEmpty(profileId)) { throw new ArgumentNullException($"profileId is null. userId={userId}"); } var profile = await this._profileService.GetProfile(profileId); if (profile == null) { profile = await CreatePlayerProfile(ctx.RemotePeer); } if (profile != null) { EnsureUserOwnsProfile(userId, profile, "User {0} cannot retrieve a profile owned by user {1}."); } //_logger.Log(LogLevel.Debug, "profiles", "Get single ", new { metadata = ctx.RemotePeer.Metadata }); if (!ctx.RemotePeer.Metadata.ContainsKey("bfg.profiles.compression")) { ctx.SendValue(profile); } else { ctx.SendValue(s => { using (var lzStream = new LZ4.LZ4Stream(s, LZ4.LZ4StreamMode.Compress, LZ4.LZ4StreamFlags.HighCompression)) { ctx.RemotePeer.Serializer().Serialize(profile, lzStream); } }); } }
private Task OnExecuteTransaction(RequestContext <IScenePeer> ctx) { var input = ctx.ReadObject <UpdateDto>(); Task <UpdateResponseDto> parameterTask; var action = OnUpdateGame; if (action != null) { parameterTask = TaskExtensions.InvokeWrapping(action, input).ContinueWith(task => { if (!task.IsFaulted) { return(new UpdateResponseDto { Success = true, Hash = task.Result }); } else { _logger.Error(task.Exception); return(new UpdateResponseDto { Success = false, Reason = task.Exception.InnerExceptions[0].Message }); } }); } else { parameterTask = TaskHelper.FromResult(new UpdateResponseDto { Success = false, Reason = "No callback has been registered for updating game." }); _logger.Log(Diagnostics.LogLevel.Error, "TransactionBroker", "No callback has been registered for updating game."); } return(parameterTask.Then(parameter => ctx.SendValue(parameter, PacketPriority.MEDIUM_PRIORITY))); }
// OnPlay is a response procedure handling the "play" rpc request sent by a client // An Rpc response procedure is created by server when an rpc request is received. These are totaly asynchronous so it can handle multiple rpc at the same time. // Here, we receive a Connection Data Transfert Object and send back an int to tell the client whether he can play or not. // private Task OnPlay(RequestContext<IScenePeerClient> ctx) { // we use RequestContext.ReadObject<Type>() to deserialize to data received from the Client. string name = ctx.ReadObject<ConnectionDtO>().name.ToLower(); bool nameAlreadyTaken = false; foreach (var p in _players.Values) { string temp = p.name.ToLower(); if (temp == name) { nameAlreadyTaken = true; break; } } if (nameAlreadyTaken == false) { var player = new Player(name); _log.Debug("main", "client joined with name : " + player.name); _players.TryAdd(ctx.RemotePeer.Id, player); ctx.SendValue(0); } else { ctx.SendValue(1); } return Task.FromResult(true); }
private Task onJoinGame(RequestContext<IScenePeerClient> packet) { GameInfos game = packet.ReadObject<GameInfos>(); game = m_games.Find(npc => { return (npc.IDOwner == game.IDOwner); }); if (game.Playing || game.IDPlayers.Count == game.MaxPlayer) { packet.SendValue(null); return Task.FromResult(true); } game.IDPlayers[packet.RemotePeer.Id] = m_players[packet.RemotePeer]; foreach (IScenePeerClient client in m_scene.RemotePeers) foreach (long key in game.IDPlayers.Keys) if (client.Id == key && key != packet.RemotePeer.Id) client.Send("UpdateGame", game); packet.SendValue(game); return Task.FromResult(true); }
private async Task CreateAccount(RequestContext<IScenePeerClient> p, ISceneHost scene) { try { var userService = scene.GetComponent<IUserService>(); var rq = p.ReadObject<CreateAccountRequest>(); scene.GetComponent<ILogger>().Log(LogLevel.Trace, "user.provider.loginpassword", "Creating user " + rq.Login + ".", rq.Login); ValidateLoginPassword(rq.Login, rq.Password); var user = await userService.GetUserByClaim(PROVIDER_NAME, "login", rq.Login); if (user != null) { scene.GetComponent<ILogger>().Log(LogLevel.Trace, "user.provider.loginpassword", "User with login " + rq.Login + " already exists.", rq.Login); throw new ClientException("An user with this login already exist."); } user = await userService.GetUser(p.RemotePeer); if (user == null) { try { var uid = PROVIDER_NAME + "-" + rq.Login; user = await userService.CreateUser(uid, JObject.Parse(rq.UserData)); } catch (Exception ex) { scene.GetComponent<ILogger>().Log(LogLevel.Trace, "user.provider.loginpassword", "Couldn't create user " + rq.Login + ".", ex); throw new ClientException("Couldn't create account : " + ex.Message); } } var salt = GenerateSaltValue(); try { await userService.AddAuthentication(user, PROVIDER_NAME, JObject.FromObject(new { login = rq.Login, email = rq.Email, salt = salt, password = HashPassword(rq.Password, salt), validated = false, })); } catch (Exception ex) { scene.GetComponent<ILogger>().Log(LogLevel.Trace, "user.provider.loginpassword", "Couldn't link account " + rq.Login + ".", ex); throw new ClientException("Couldn't link account : " + ex.Message); } scene.GetComponent<ILogger>().Log(LogLevel.Trace, "user.provider.loginpassword", "Creating user " + rq.Login + ".", rq.Login); p.SendValue(new LoginResult { Success = true }); } catch (Exception ex) { p.SendValue(new LoginResult { ErrorMsg = ex.Message, Success = false }); } }
private async Task<bool> UseSkillImpl(RequestContext<IScenePeerClient> arg) { var env = _scene.GetComponent<IEnvironment>(); var p = arg.ReadObject<UserSkillRequest>(); var ship = _ships[_players[arg.RemotePeer.Id].ShipId]; if (ship.Status != ShipStatus.InGame || ship.currentPv <= 0) { return false; //throw new ClientException("You can only use skills during games."); } var timestamp = _scene.GetComponent<IEnvironment>().Clock; var weapon = ship.weapons.FirstOrDefault(w => w.id == p.skillId); if (weapon == null) { return false; //throw new ClientException(string.Format("Skill '{0}' not available.", p.skillId)); } if (weapon.fireTimestamp + weapon.coolDown > timestamp) { return false; //throw new ClientException("Skill in cooldown."); } if (!_ships.ContainsKey(p.target)) { return false; } var target = _ships[p.target]; if (target.Status != ShipStatus.InGame) { return false; //throw new ClientException("Can only use skills on ships that are in game."); } var dx = ship.x - target.x; var dy = ship.y - target.y; if (weapon.range * weapon.range < dx * dx + dy * dy) { return false; //throw new ClientException("Target out of range."); } weapon.fireTimestamp = timestamp; var success = _rand.Next(100) < weapon.precision * 100; if (success) { if (target.currentPv > 0) { target.ChangePv(-weapon.damage); } } this.RegisterSkill(ship.id, target.id, success, weapon.id, timestamp); arg.SendValue(new UseSkillResponse { error = false, errorMsg = null, skillUpTimestamp = weapon.fireTimestamp + weapon.coolDown, success = success }); return true; }
public async Task FindMatch(RequestContext <IScenePeerClient> request) { _logger.Log(LogLevel.Trace, "matchmaker", "received a matchmaking request", new { }); var group = new Group(); var provider = request.ReadObject <string>(); var currentUser = await _sessions.GetUser(request.RemotePeer); foreach (var extractor in _extractors) { if (await extractor.ExtractData(provider, request, group)) { break; } } _logger.Log(LogLevel.Trace, "matchmaker", "data extracted from the matchmaking request", new { group }); foreach (var p in group.Players) { if (_usersToGroup.ContainsKey(p.UserId)) { throw new ClientException($"'{p.UserId} is already waiting for a match."); } } var state = new MatchmakingRequestState(group); _waitingGroups[group] = state; foreach (var user in group.Players) { _usersToGroup[user.UserId] = group; } request.CancellationToken.Register(() => { state.Tcs.TrySetCanceled(); }); var memStream = new MemoryStream(); request.InputStream.Seek(0, SeekOrigin.Begin); request.InputStream.CopyTo(memStream); await BroadcastToPlayers(group, UPDATE_FINDMATCH_REQUEST_PARAMS_ROUTE, (s, sz) => { memStream.Seek(0, System.IO.SeekOrigin.Begin); memStream.CopyTo(s); }); await BroadcastToPlayers(group, UPDATE_NOTIFICATION_ROUTE, (s, sz) => { s.WriteByte((byte)MatchmakingStatusUpdate.SearchStart); }); state.State = RequestState.Ready; IMatchResolverContext resolutionContext; try { resolutionContext = await state.Tcs.Task; } catch (TaskCanceledException) { await BroadcastToPlayers(group, UPDATE_NOTIFICATION_ROUTE, (s, sz) => s.WriteByte((byte)MatchmakingStatusUpdate.Cancelled)); } finally //Always remove group from list. { MatchmakingRequestState _; foreach (var player in group.Players) { Group grp1; _usersToGroup.TryRemove(player.UserId, out grp1); } _waitingGroups.TryRemove(group, out _); if (_.Candidate != null) { MatchReadyCheck rc; if (_pendingReadyChecks.TryGetValue(_.Candidate.Id, out rc)) { if (!rc.RanToCompletion) { rc.Cancel(currentUser.Id); } } } } }
/// <summary> /// Invite an user in the group of the connected player /// </summary> /// <param name="ctx"></param> /// <returns></returns> public async Task Invite(RequestContext <IScenePeerClient> ctx) { var currentUser = await GetUser(); var targetUserId = ctx.ReadObject <string>(); int userDataLength = (int)(ctx.InputStream.Length - ctx.InputStream.Position); if (userDataLength > MaxInvitationUserDataLength) { throw new ClientException($"User data too big ({userDataLength} bytes). Maximum : {MaxInvitationUserDataLength} bytes"); } var userData = new byte[userDataLength]; ctx.InputStream.Read(userData, 0, userDataLength); //Check if user is connected var targetPeer = await _users.GetPeer(targetUserId); if (targetPeer == null) { throw new ClientException("The target user is not connected."); } _logger.Log(LogLevel.Trace, "group.invite", $"'{currentUser.Id}' inviting '{targetUserId}'", new { user = currentUser.Id, target = targetUserId }); //Create a group whose leader is currentUser var group = Group.CreateGroup(currentUser.Id); await _groupsIndex.TryAdd(group.Id, group); //Get the current group of current user, and set it to the just created group if it doesn't exist. var groupResult = await _userToGroupIndex.GetOrAdd(currentUser.Id, new UserGroupData(group.Id, GroupMemberState.InGroup)); if (groupResult.Value.State != GroupMemberState.InGroup) { throw new ClientException("You can't invite players when you have a pending invitation running yourself."); } //If the group of the current user is not the group that we just created, we are in one of these situations: // * The current user is already in a group of 1 or several players, in this case this group is the one we should select. // * The current user is associated in a way or another to a an invalid group : We should update the link with the new group. if (groupResult.Value.GroupId != group.Id) { var r = await _groupsIndex.TryGet(groupResult.Value.GroupId); if (r.Success)//The group that existed prior to the invitation process started is valid. We use it instead of the one we just created. { await _groupsIndex.TryRemove(group.Id); group = r.Value; } else//The group associated with the current player doesn't exist. We should replace the player-group association. { if (!(await _userToGroupIndex.TryUpdate(currentUser.Id, new UserGroupData(group.Id, GroupMemberState.InGroup), groupResult.Version)).Success) { throw new ClientException("An error occured. Please retry."); } } } //Set the target player in pending invitation state var targetResult = await _userToGroupIndex.GetOrAdd(targetUserId, new UserGroupData(group.Id, GroupMemberState.PendingInvitation)); while (targetResult.Value.GroupId != group.Id) { // The user already has an userToGroupIndex // * She has a pending invitation // * The userToGroupIndex is unsynchronized, the linked group doesn't exist. // * She is already in a group // * She is alone in her group. In this case, the former group must be cleant if (targetResult.Value.State == GroupMemberState.PendingInvitation) { throw new ClientException("The player has already a pending invitation."); } else { var targetGroup = await _groupsIndex.TryGet(targetResult.Value.GroupId); // Find the current group of the invited player if (!targetGroup.Success) //The group doesn't exist. The userToGroupIndex must be cleant. { await _userToGroupIndex.TryRemove(targetUserId); targetResult = await _userToGroupIndex.GetOrAdd(targetUserId, new UserGroupData(group.Id, GroupMemberState.PendingInvitation)); continue; } else if (targetGroup.Value.Members.Count == 1)//Alone in a group. Clean it! { await _groupsIndex.TryRemove(targetResult.Value.GroupId); await _userToGroupIndex.TryRemove(targetUserId); targetResult = await _userToGroupIndex.GetOrAdd(targetUserId, new UserGroupData(group.Id, GroupMemberState.PendingInvitation)); continue; } else//Grouped with other players. Failure! { throw new ClientException("The player is already in a group."); } } } //Try to add the invitation to the group, and check if the group is not full. group = await AddMemberToGroup(group.Id, targetUserId); var invitationAccepted = false; try { ////Broadcast to all group member that an invitation is pending. //await BroadcastToGroupMembers(group, "group.members.invitationPending", s => //{ // using (var writer = new System.IO.BinaryWriter(s, Encoding.UTF8, true)) // { // writer.Write(targetUserId); // writer.Write(userData); // } //}); var tcs = new TaskCompletionSource <bool>(); var disposable = targetPeer.Rpc("group.invite.accept", s => { ctx.InputStream.CopyTo(s); }, Stormancer.Core.PacketPriority.MEDIUM_PRIORITY).Subscribe(p => { tcs.TrySetResult(p.Stream.ReadByte() != 0); }); invitationAccepted = await tcs.Task; } catch (Exception ex) { _logger.Log(LogLevel.Error, "groups.invite", $"An error occured while inviting '{targetUserId}' to group '{group.Id}'", ex); } finally { await BroadcastToGroupMembers(group, "group.members.invitationComplete", s => { using (var writer = new System.IO.BinaryWriter(s, Encoding.UTF8, true)) { writer.Write(targetUserId); writer.Write(invitationAccepted); } }); } if (!invitationAccepted)//Refused invitation or error { await RemoveMemberFromGroup(group.Id, targetUserId); await _userToGroupIndex.TryRemove(targetUserId); throw new ClientException("The player refused the invitation."); } else { await _userToGroupIndex.UpdateWithRetries(targetUserId, d => d.UpdateState(GroupMemberState.InGroup)); var r = await _groupsIndex.UpdateWithRetries(group.Id, g => g.AcceptInvitation(targetUserId)); group = r.Value; } await BroadCastGroupUpdate(group); }
public Task OnRegisterObject(RequestContext<IScenePeerClient> ctx) { _log.Debug("replicator", "registering object"); var dto = ctx.ReadObject<ReplicatorDTO>(); var obj = new ReplicatorObject(); obj.Client = ctx.RemotePeer; obj.PrefabId = dto.PrefabId; obj.Id = Ids++; dto.Id = obj.Id; ctx.SendValue<ReplicatorDTO>(dto); foreach(IScenePeerClient client in _scene.RemotePeers) { if (client.Id != ctx.RemotePeer.Id) { client.Send<ReplicatorDTO>("CreateObject", dto); } } return Task.FromResult(true); }