private async Task<bool> MoveCoreAsync(Point sourcePosition, Point targetPosition, ITransactionWithMoveSupport transaction) { var source = await GetAsync(sourcePosition); var target = await GetAsync(targetPosition); var oldSource = source; var oldTarget = target; source.Tile.EnsureNotNull(); target.Tile.EnsureNotNull(); if (source.Tile.Entity == Entity.None) { // Cancel if there's no entity to move transaction.IsSealed = true; return false; } var move = new EntityMoveInfo(source.Tile.Entity, sourcePosition, targetPosition); transaction.Moves.Push(move); IBeginMoveArgs beginMoveArgs = new GameplayArgs(transaction, this); IDetachArgs detachArgs = new GameplayArgs(transaction, this); IAttachArgs attachArgs = new GameplayArgs(transaction, this); // OnBeginMove: Notify entity that a move has been initiated await source.Tile.Entity.BeginMoveAsync(beginMoveArgs); beginMoveArgs.ValidateResult(); if (transaction.IsSealed) return false; var newSource = source.WithTile(Tile.Compose(source.Tile, beginMoveArgs.ResultingEntity)); if (transaction.Set(sourcePosition, source, newSource)) source = newSource; move.Entity = beginMoveArgs.ResultingEntity; // Detach: Remove the entity from the source tile await source.Tile.DetachEntityAsync(detachArgs); detachArgs.ValidateResult(); if (transaction.IsSealed) return false; // Attach: Add the entity to the target tile await target.Tile.AttachEntityAsync(attachArgs); attachArgs.ValidateResult(); if (transaction.IsSealed) return false; // Detach from old tile, attach to new tile have succeeded // => Apply changes to transaction var newSource2 = source.WithTile(detachArgs.Result); if (transaction.Set(sourcePosition, source, newSource2)) source = newSource2; var newTarget = target.WithTile(attachArgs.Result); if (transaction.Set(targetPosition, target, newTarget)) target = newTarget; // Emit events var moveEvent = new EntityMoveEvent(sourcePosition, targetPosition, oldSource.Tile, target.Tile); transaction.Emit(moveEvent); if (source.Tile.Entity != Entity.None) { // A new entity has been created at source position var spawnEvent = new EntitySpawnEvent(sourcePosition, source.Tile.Entity); transaction.Emit(spawnEvent); } if (oldTarget.Tile.Entity != Entity.None) { // The entity at target position has either been replaced // or it has been moved/collected/... during a nested move var oldTargetEntityOverwritten = !(transaction.Events.OfType<EntityDespawnEvent>().Any(ev => ev.Position == targetPosition) || transaction.Events.OfType<EntityMoveEvent>().Any(ev => ev.SourcePosition == targetPosition)); if (oldTargetEntityOverwritten) { // If replaced, emit despawn event var despawnEvent = new EntityDespawnEvent(targetPosition, oldTarget.Tile.Entity); transaction.Emit(despawnEvent); } } transaction.Moves.Pop(); return true; }
private async void OnChunkLoaded(KeyValuePair<Point, IChunk> kvp) { for (var y = 0; y < Chunk.Size; y++) { for (var x = 0; x < Chunk.Size; x++) { var localPosition = new Point(x, y); var globalPosition = kvp.Key * Chunk.Size + localPosition; var tileInfo = kvp.Value[localPosition]; Dependencies.AddDependenciesOf(tileInfo.Tile, globalPosition); // For entities emit a spawn event if (tileInfo.Tile.Entity != Entity.None) { var spawnEvent = new EntitySpawnEvent(globalPosition, tileInfo.Tile.Entity); _eventSource.OnNext(spawnEvent); } } } // Properly initialize the chunk. // Example scenarios: On chunk loading... // - a button should be activated immediately if there's an entity pressing it // - a balloon should be popped immediately if it is on a pin var chunkPoints = new Rectangle(kvp.Key.X * Chunk.Size, kvp.Key.Y * Chunk.Size, Chunk.Size - 1, Chunk.Size - 1); var transaction = new TransactionWithMoveSupport(MoveInitiator.Empty); await UpdateTilesAsync(chunkPoints, transaction); _eventSource.OnNext(new ChunkAddedEvent(kvp.Value)); // TODO: This is problematic! A commit is done directly on the map // bypassing the GameServer. We might have to forward the new chunk // to some of the players. // On client side we can't just invent some new version numbers. await transaction.CommitAsync(this, Metadata?.NextTileVersion() ?? 0); }
private void OnEntitySpawned(EntitySpawnEvent e) { var entityInfo = new EntityInfo(e.Entity, e.Position, Map.Events); lock (_entitiesLock) _entities.Add(entityInfo); if ((e.Entity as PlayerEntity)?.PlayerId == FollowedPlayerId && FollowedPlayerId != null) CameraPosition = e.Position.ToVector2() + new Vector2(.5f, -.5f); }