Skip to content
This repository has been archived by the owner on Dec 18, 2018. It is now read-only.

Commit

Permalink
Hacks from hell
Browse files Browse the repository at this point in the history
  • Loading branch information
davidfowl committed Jan 18, 2017
1 parent ba60b03 commit ccabbbc
Show file tree
Hide file tree
Showing 6 changed files with 246 additions and 12 deletions.
6 changes: 0 additions & 6 deletions samples/SocketsSample/Hubs/Chat.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,6 @@ public async Task Send(string message)
await Clients.All.InvokeAsync("Send", $"{Context.ConnectionId}: {message}");
}

public async Task Add(int a, int b)
{
var result = await Clients.Client(Context.ConnectionId).InvokeAsync("Add", a, b);
await Clients.All.InvokeAsync("Send", $"Added {a} + {b} on client {Context.ConnectionId} = {result}");
}

public Task SendToGroup(string groupName, string message)
{
return Clients.Group(groupName).InvokeAsync("Send", $"{Context.ConnectionId}@{groupName}: {message}");
Expand Down
119 changes: 119 additions & 0 deletions samples/SocketsSample/Hubs/Game.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.SignalR;

namespace SocketsSample.Hubs
{
public class GameLogic
{
private readonly HashSet<string> _connections = new HashSet<string>();
private bool _gameRunning;
private readonly IHubContext<Game> _hubContext;
private Task _gameTask;

public GameLogic(IHubContext<Game> hubContext)
{
_hubContext = hubContext;
}

public void RemoveConnection(string connectionId)
{
lock (_connections)
{
_connections.Remove(connectionId);
}
}

public void AddConnection(string connectionId)
{
lock (_connections)
{
_connections.Add(connectionId);

if (_connections.Count == 2 && !_gameRunning)
{
_gameTask = RunGame();
}
}
}

private async Task RunGame()
{
_gameRunning = true;

// Start the game on all clients
var winner = await Task.WhenAny(InvokeOnAll("Ask"));

var result = await winner;

var ignore = _hubContext.Clients.Client(result.ConnectionId).InvokeAsync("Send", "You win :)");

foreach (var item in _connections)
{
if (item == result.ConnectionId)
{
continue;
}

ignore = _hubContext.Clients.Client(item).InvokeAsync("Send", "You lose :(");
}
}

private Task<Result>[] InvokeOnAll(string method, params object[] args)
{
Task<Result>[] tasks;
lock (_connections)
{
tasks = new Task<Result>[_connections.Count];

int i = 0;
foreach (var id in _connections)
{
tasks[i++] = Invoke(id, method, args);
}
}

return tasks;
}

private async Task<Result> Invoke(string id, string method, object[] args)
{

return new Result
{
Value = await _hubContext.Clients.Client(id).InvokeAsync(method, args),
ConnectionId = id
};
}

private class Result
{
public object Value { get; set; }
public string ConnectionId { get; set; }
}
}

public class Game : Hub
{
private readonly GameLogic _gameLogic;

public Game(GameLogic gameLogic)
{
_gameLogic = gameLogic;
}

public override Task OnConnectedAsync()
{
_gameLogic.AddConnection(Context.ConnectionId);
return base.OnConnectedAsync();
}

public override Task OnDisconnectedAsync(Exception exception)
{
_gameLogic.RemoveConnection(Context.ConnectionId);
return base.OnDisconnectedAsync(exception);
}
}
}
2 changes: 2 additions & 0 deletions samples/SocketsSample/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ public void ConfigureServices(IServiceCollection services)
});
// .AddRedis();

services.AddSingleton<GameLogic>();
services.AddSingleton<MessagesEndPoint>();
services.AddSingleton<ProtobufSerializer>();
services.AddSingleton<IHostedService, ClockService>();
Expand All @@ -51,6 +52,7 @@ public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerF
{
routes.MapHub<Chat>("/hubs");
routes.MapHub<Clock>("/clock");
routes.MapHub<Game>("/game");
});

app.UseSockets(routes =>
Expand Down
103 changes: 103 additions & 0 deletions samples/SocketsSample/wwwroot/game.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
</head>
<body>
<h1 id="head1"></h1>

<input type="button" id="game" value="Click me!" disabled="disabled" />

<ul id="messages"></ul>
</body>
</html>
<script src="lib/signalr-client/signalr-client.js"></script>
<script>
var isConnected = false;
function getParameterByName(name, url) {
if (!url) {
url = window.location.href;
}
name = name.replace(/[\[\]]/g, "\\$&");
var regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"),
results = regex.exec(url);
if (!results) return null;
if (!results[2]) return '';
return decodeURIComponent(results[2].replace(/\+/g, " "));
}

function click(id, callback) {
document.getElementById(id).addEventListener('click', event => {
callback(event);
event.preventDefault();
});
}

function addLine(line, color) {
var child = document.createElement('li');
if (color) {
child.style.color = color;
}
child.innerText = line;
document.getElementById('messages').appendChild(child);
}

function invoke(connection, method, ...args) {
if (!isConnected) {
return;
}
var argsArray = Array.prototype.slice.call(arguments);
connection.invoke.apply(connection, argsArray.slice(1))
.then(result => {
console.log("invocation completed successfully: " + (result === null ? '(null)' : result));

if (result) {
addLine(result);
}
})
.catch(err => {
addLine(err, 'red');
});
}

let transport = getParameterByName('transport') || 'webSockets';

document.getElementById('head1').innerHTML = transport;

let connection = new signalR.HubConnection(`http://${document.location.host}/game`, 'formatType=json&format=text');

var currentQuestion = {
resolve: function () { },
reject: function() { }
};

connection.on('Ask', (a, b) => {
document.getElementById('game').removeAttribute('disabled');
return new Promise(function (resolve, reject) {
currentQuestion.resolve = resolve;
currentQuestion.reject = reject;
});
});

connection.on('Send', data => {
alert(data);
});


connection.connectionClosed = e => {
if (e) {
addLine('Connection closed with error: ' + e, 'red');
}
else {
addLine('Disconnected', 'green');
}
}

click('game', event => {
currentQuestion.resolve();
});

connection.start(transport);

</script>
1 change: 1 addition & 0 deletions samples/SocketsSample/wwwroot/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ <h1>ASP.NET SignalR</h1>
<ul>
<li><a href="clock.html">Server Clock</a></li>
<li><a href="hubs.html">Hubs</a></li>
<li><a href="game.html">Game</a></li>
</ul>
</body>
</html>
27 changes: 21 additions & 6 deletions src/Microsoft.AspNetCore.SignalR.Client.TS/HubConnection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,12 +56,27 @@ export class HubConnection {
// TODO: bind? args?
let result = method.apply(this, invocation.Arguments);
if (invocation.Id) {
let invocationResultDescriptor: InvocationResultDescriptor = {
"Id": invocation.Id.toString(),
"Result": result,
"Error": null
};
this.connection.send(JSON.stringify(invocationResultDescriptor));
if (result && result.constructor && result.constructor.name === "Promise")

This comment has been minimized.

Copy link
@DamianEdwards

DamianEdwards Jan 19, 2017

Member

Turns out we should've used a "type guard" here: https://www.typescriptlang.org/docs/handbook/advanced-types.html

if (result instanceof Promise) {

This comment has been minimized.

Copy link
@moozzyk

moozzyk Jan 19, 2017

Contributor

Will update.

{
let promise: Promise<void> = result;

promise.then(() => {
let invocationResultDescriptor: InvocationResultDescriptor = {
"Id": invocation.Id.toString(),
"Result": null,
"Error": null
};
this.connection.send(JSON.stringify(invocationResultDescriptor));
});
}
else {
let invocationResultDescriptor: InvocationResultDescriptor = {
"Id": invocation.Id.toString(),
"Result": result || null,
"Error": null
};
this.connection.send(JSON.stringify(invocationResultDescriptor));
}
}
}
}
Expand Down

0 comments on commit ccabbbc

Please sign in to comment.