Compare commits
No commits in common. "f728240db9efb1860d7ef1b6877e0e9016d2e490" and "1254e32749ae3f8d1d05f65233b641eca874e87f" have entirely different histories.
f728240db9
...
1254e32749
@ -1,8 +0,0 @@
|
||||
namespace PongGame;
|
||||
|
||||
public enum GameStatus {
|
||||
WaitingForPlayers,
|
||||
InProgress,
|
||||
Finished,
|
||||
Paused,
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
namespace PongGame.Hubs;
|
||||
|
||||
public interface IPongClient {
|
||||
Task GameStateChanged(GameStatus state);
|
||||
Task ReceiveGameState(PongGameState state);
|
||||
Task UsernameChanged(string value);
|
||||
}
|
@ -1,110 +0,0 @@
|
||||
using System.Drawing;
|
||||
|
||||
namespace PongGame.Hubs;
|
||||
|
||||
/// <summary>
|
||||
/// Pong game state saving the positions of the paddles and the ball.
|
||||
/// The Pong board has aspect ratio 1:2 with height 500 and width 1000.
|
||||
/// </summary>
|
||||
public struct PongGameState {
|
||||
public const float HEIGHT = 500.0f;
|
||||
public const float WIDTH = 2 * HEIGHT;
|
||||
public const float PADDLE1_OFFSET = WIDTH / 20;
|
||||
public const float PADDLE2_OFFSET = WIDTH - PADDLE1_OFFSET;
|
||||
|
||||
public static readonly PongGameState Initial = new() {
|
||||
BallState = PongBallState.Initial,
|
||||
Paddle1 = PongPaddleState.Initial,
|
||||
Paddle2 = PongPaddleState.Initial,
|
||||
Status = GameStatus.WaitingForPlayers,
|
||||
WinnerLeft = null
|
||||
};
|
||||
|
||||
public PongPaddleState Paddle1;
|
||||
public PongPaddleState Paddle2;
|
||||
public PongBallState BallState;
|
||||
public GameStatus Status;
|
||||
public bool? WinnerLeft;
|
||||
|
||||
public static void Update(ref PongGameState state) {
|
||||
if (state.Status is not GameStatus.InProgress) return;
|
||||
|
||||
PongPaddleState.Update(ref state.Paddle1);
|
||||
PongPaddleState.Update(ref state.Paddle2);
|
||||
PongBallState.Update(ref state.BallState);
|
||||
var ballX = state.BallState.Pos.X;
|
||||
if (ballX is < 0 or > WIDTH) {
|
||||
state.WinnerLeft = ballX < 0;
|
||||
state.Status = GameStatus.Finished;
|
||||
return;
|
||||
}
|
||||
|
||||
Collide(in state.Paddle1, ref state.BallState, true);
|
||||
Collide(in state.Paddle2, ref state.BallState, false);
|
||||
}
|
||||
|
||||
private static void Collide(in PongPaddleState paddle, ref PongBallState ballState, bool left) {
|
||||
var paddleX = left ? PADDLE1_OFFSET : PADDLE2_OFFSET;
|
||||
var intersection = RectangleF.Intersect(paddle.GetCollider(paddleX), ballState.GetCollider());
|
||||
if (intersection.IsEmpty) return;
|
||||
|
||||
// TODO: continuous collision
|
||||
var ratio = (ballState.Pos.Y - paddle.Height + PongPaddleState.PADDLE_HALF_LENGTH) / PongPaddleState.PADDLE_LENGTH;
|
||||
|
||||
// TODO: lesser angles
|
||||
var upAngle = left ? MathF.PI * 3 / 8 : MathF.PI * 5 / 8;
|
||||
var downAngle = left ? -MathF.PI * 3 / 8 : MathF.PI * 11 / 8;
|
||||
|
||||
// TODO: reflect ball on surface instead of launching
|
||||
ballState.BallAngle = ratio * downAngle + (1 - ratio) * upAngle;
|
||||
}
|
||||
|
||||
public struct PongBallState {
|
||||
public const float BALL_SPEED = 2 * BALL_RADIUS;
|
||||
public const float BALL_RADIUS = HEIGHT / 125;
|
||||
|
||||
public static readonly PongBallState Initial = new() {
|
||||
BallAngle = 0.0f,
|
||||
Pos = new() {
|
||||
X = WIDTH / 2,
|
||||
Y = HEIGHT / 2
|
||||
}
|
||||
};
|
||||
|
||||
public PointF Pos;
|
||||
public float BallAngle;
|
||||
|
||||
public static void Update(ref PongBallState state) {
|
||||
var (dy, dx) = MathF.SinCos(state.BallAngle);
|
||||
state.Pos.X += BALL_SPEED * dx;
|
||||
state.Pos.Y -= BALL_SPEED * dy;
|
||||
|
||||
if (state.Pos.Y < BALL_RADIUS
|
||||
|| state.Pos.Y > HEIGHT - BALL_RADIUS)
|
||||
state.BallAngle = 2 * MathF.PI - state.BallAngle;
|
||||
}
|
||||
|
||||
public RectangleF GetCollider() => new(Pos.X - BALL_RADIUS, Pos.Y - BALL_RADIUS, 2 * BALL_RADIUS, 2 * BALL_RADIUS);
|
||||
}
|
||||
|
||||
public struct PongPaddleState {
|
||||
public const float PADDLE_LENGTH = HEIGHT / 10;
|
||||
public const float PADDLE_HALF_LENGTH = PADDLE_LENGTH / 2;
|
||||
public const float PADDLE_WIDTH = PADDLE_LENGTH / 5;
|
||||
public const float PADDLE_SPEED = 8;
|
||||
|
||||
public static readonly PongPaddleState Initial = new() {
|
||||
Direction = PongPaddleDirection.Stop,
|
||||
Height = HEIGHT / 2
|
||||
};
|
||||
|
||||
public float Height;
|
||||
public PongPaddleDirection Direction;
|
||||
|
||||
public static void Update(ref PongPaddleState state) {
|
||||
state.Height = Math.Clamp(state.Height - ((int)state.Direction) * PADDLE_SPEED, PADDLE_HALF_LENGTH, HEIGHT - PADDLE_HALF_LENGTH);
|
||||
}
|
||||
|
||||
public RectangleF GetCollider(float x) => new(x - PADDLE_WIDTH / 2, Height - PADDLE_HALF_LENGTH, PADDLE_WIDTH, PADDLE_LENGTH);
|
||||
}
|
||||
}
|
@ -1,76 +0,0 @@
|
||||
using Microsoft.AspNetCore.SignalR;
|
||||
|
||||
namespace PongGame.Hubs;
|
||||
|
||||
public class PongHub : Hub<IPongClient> {
|
||||
private const string PLAYER_KEY = "PLAYER";
|
||||
private readonly PongLobby Lobby;
|
||||
private readonly ILogger<PongHub> Logger;
|
||||
|
||||
public PongHub(PongLobby lobby, ILogger<PongHub> logger) : base() {
|
||||
Lobby = lobby;
|
||||
Logger = logger;
|
||||
}
|
||||
|
||||
private PongPlayer Player {
|
||||
get => (Context.Items.TryGetValue(PLAYER_KEY, out var player) ? player as PongPlayer : null)
|
||||
?? throw new InvalidProgramException("Player was not assigned at connection start!");
|
||||
set => Context.Items[PLAYER_KEY] = value;
|
||||
}
|
||||
|
||||
public override Task OnConnectedAsync() {
|
||||
Player = Lobby.CreatePlayer();
|
||||
Player.Client = Clients.Client(Context.ConnectionId);
|
||||
Player.Username = "Anon";
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private void AssertNotInRoom() {
|
||||
if (Player.ConnectedRoom is PongRoom currentRoom)
|
||||
throw new HubException($"User is already connected to room [{currentRoom}]");
|
||||
}
|
||||
|
||||
private PongRoom AssertInRoom() {
|
||||
if (Player.ConnectedRoom is not PongRoom currentRoom)
|
||||
throw new HubException($"User is not in any room!");
|
||||
return currentRoom;
|
||||
}
|
||||
|
||||
public Task<string> CreateRoom() {
|
||||
AssertNotInRoom();
|
||||
var room = Lobby.CreateRoom(Player);
|
||||
return Task.FromResult(room.ID);
|
||||
}
|
||||
|
||||
public Task<string> JoinRoom(string roomId) {
|
||||
AssertNotInRoom();
|
||||
var room = Lobby.JoinRoom(Player, roomId);
|
||||
return Task.FromResult(room.ID);
|
||||
}
|
||||
|
||||
public Task MovePaddle(int dir) {
|
||||
var room = AssertInRoom();
|
||||
var direction = (PongPaddleDirection)dir;
|
||||
if (!Enum.IsDefined(direction))
|
||||
throw new HubException($"Invalid direction: {dir}!");
|
||||
room.MovePaddle(Player, direction);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task LeaveRoom() {
|
||||
Lobby.LeaveRoom(Player);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task RequestUsernameChange(string username) {
|
||||
// TOOD: check this
|
||||
Logger.LogInformation($"Player {Player} requested username change to [{username}]");
|
||||
Player.Username = username;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public override Task OnDisconnectedAsync(Exception? exception) {
|
||||
Lobby.RemovePlayer(Player);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
@ -1,65 +0,0 @@
|
||||
using System.Collections.Concurrent;
|
||||
using Microsoft.AspNetCore.SignalR;
|
||||
|
||||
namespace PongGame.Hubs;
|
||||
|
||||
public class PongLobby {
|
||||
private readonly HashSet<PongPlayer> connectedPlayers = new();
|
||||
private readonly Dictionary<string, PongRoom> PongRooms = new();
|
||||
|
||||
public PongLobby(ILogger<PongLobby> logger)
|
||||
=> Logger = logger;
|
||||
|
||||
public const int ROOM_ID_LENGTH = 4;
|
||||
|
||||
public PongPlayer CreatePlayer() {
|
||||
var player = new PongPlayer();
|
||||
lock (connectedPlayers)
|
||||
connectedPlayers.Add(player);
|
||||
return player;
|
||||
}
|
||||
|
||||
public void RemovePlayer(PongPlayer player) {
|
||||
if (player.ConnectedRoom is PongRoom room)
|
||||
room.Leave(player);
|
||||
lock (connectedPlayers)
|
||||
_ = connectedPlayers.Remove(player);
|
||||
}
|
||||
|
||||
public PongRoom CreateRoom(PongPlayer player) {
|
||||
PongRoom room;
|
||||
lock (PongRooms) {
|
||||
room = new(GenerateRoomId(), Logger);
|
||||
PongRooms.Add(room.ID, room);
|
||||
}
|
||||
room.Join(player);
|
||||
return room;
|
||||
}
|
||||
|
||||
public PongRoom JoinRoom(PongPlayer player, string roomId) {
|
||||
PongRoom? room;
|
||||
lock (PongRooms) {
|
||||
room = PongRooms.GetValueOrDefault(roomId);
|
||||
}
|
||||
if (room is null) throw new HubException($"Room [{roomId}] not found!");
|
||||
room.Join(player);
|
||||
return room;
|
||||
}
|
||||
|
||||
public void LeaveRoom(PongPlayer player) {
|
||||
if (player.ConnectedRoom is PongRoom room)
|
||||
room.Leave(player);
|
||||
}
|
||||
|
||||
private readonly Random random = new();
|
||||
private readonly ILogger<PongLobby> Logger;
|
||||
private const string ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
|
||||
private string GenerateRoomId() {
|
||||
string id;
|
||||
do {
|
||||
id = string.Concat(Enumerable.Range(0, ROOM_ID_LENGTH).Select(_ => ALPHABET[random.Next(ALPHABET.Length)]));
|
||||
} while (PongRooms.ContainsKey(id));
|
||||
return id;
|
||||
}
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
namespace PongGame;
|
||||
|
||||
public enum PongPaddleDirection {
|
||||
Up = -1,
|
||||
Stop,
|
||||
Down,
|
||||
}
|
@ -1,22 +0,0 @@
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace PongGame.Hubs;
|
||||
|
||||
[DebuggerDisplay($"{{{nameof(ToString)}(),nq}}")]
|
||||
public class PongPlayer {
|
||||
private string username = default!;
|
||||
|
||||
public PongRoom? ConnectedRoom { get; internal set; }
|
||||
public string Username {
|
||||
get => username;
|
||||
internal set {
|
||||
if (username != value) {
|
||||
username = value;
|
||||
Task.Run(() => Client.UsernameChanged(value));
|
||||
}
|
||||
}
|
||||
}
|
||||
public IPongClient Client { get; internal set; } = default!;
|
||||
|
||||
public override string ToString() => $"[{Username}]";
|
||||
}
|
@ -1,99 +0,0 @@
|
||||
using System.ComponentModel;
|
||||
using Microsoft.AspNetCore.SignalR;
|
||||
|
||||
namespace PongGame.Hubs;
|
||||
|
||||
public class PongRoom {
|
||||
public string ID { get; }
|
||||
private readonly ILogger Logger;
|
||||
private const string JOIN_LOG_TEMPLATE = "[{ID}] {Player} joined pong room as player {Number}!";
|
||||
private const string LEAVE_LOG_TEMPLATE = "[{ID}] Player {Number} {Player} left pong room!";
|
||||
private const string DIRECTION_LOG_TEMPLATE = "[{ID}] Player {Number} {player} moves paddle in direction: {direction}!";
|
||||
|
||||
public PongRoom(string id, ILogger logger) {
|
||||
ID = id;
|
||||
Logger = logger;
|
||||
gameWorker.DoWork += GameLoop;
|
||||
}
|
||||
|
||||
public PongPlayer? Player1 { get; private set; }
|
||||
public PongPlayer? Player2 { get; private set; }
|
||||
private PongGameState State = PongGameState.Initial;
|
||||
|
||||
public void Join(PongPlayer player) {
|
||||
// TODO: synchronize this
|
||||
if (Player1 is null) {
|
||||
Player1 = player;
|
||||
Logger.LogInformation(JOIN_LOG_TEMPLATE, ID, player, 1);
|
||||
} else if (Player2 is null) {
|
||||
Player2 = player;
|
||||
Logger.LogInformation(JOIN_LOG_TEMPLATE, ID, player, 1);
|
||||
} else
|
||||
throw new HubException($"Lobby [{ID}] is already full!");
|
||||
_ = Task.Run(PlayersChanged);
|
||||
player.ConnectedRoom = this;
|
||||
}
|
||||
|
||||
public void Leave(PongPlayer player) {
|
||||
if (Player1 == player) {
|
||||
Player1 = null;
|
||||
Logger.LogInformation(LEAVE_LOG_TEMPLATE, ID, 1, player);
|
||||
} else if (Player2 == player) {
|
||||
Player2 = null;
|
||||
Logger.LogInformation(LEAVE_LOG_TEMPLATE, ID, 2, player);
|
||||
}
|
||||
player.ConnectedRoom = null;
|
||||
_ = Task.Run(PlayersChanged);
|
||||
}
|
||||
|
||||
private BackgroundWorker gameWorker = new() {
|
||||
WorkerSupportsCancellation = true
|
||||
};
|
||||
|
||||
private void GameLoop(object? sender, DoWorkEventArgs e) {
|
||||
while (!gameWorker.CancellationPending
|
||||
&& Player1 is PongPlayer p1
|
||||
&& Player2 is PongPlayer p2
|
||||
&& State.Status is GameStatus.InProgress) {
|
||||
PongGameState.Update(ref State);
|
||||
_ = Task.Run(() => Task.WhenAll(
|
||||
p1.Client.ReceiveGameState(State),
|
||||
p2.Client.ReceiveGameState(State)));
|
||||
Thread.Sleep(1000 / 60);
|
||||
}
|
||||
}
|
||||
|
||||
private Task PlayersChanged() {
|
||||
if (Player1 is PongPlayer player1 && Player2 is PongPlayer player2) {
|
||||
Logger.LogInformation("[{ID}] Pong game started: {player1} vs. {player2}", ID, player1, player2);
|
||||
ResumeGame();
|
||||
} else if (Player1 is null && Player2 is null) {
|
||||
CloseRoom();
|
||||
} else
|
||||
PauseGame();
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private void ResumeGame() {
|
||||
State.Status = GameStatus.InProgress;
|
||||
if (!gameWorker.IsBusy) gameWorker.RunWorkerAsync();
|
||||
}
|
||||
|
||||
private void PauseGame() => State.Status = GameStatus.Paused;
|
||||
private void CloseRoom() => State.Status = GameStatus.Finished;
|
||||
|
||||
public override string ToString() => $"[{ID}]";
|
||||
|
||||
public void MovePaddle(PongPlayer player, PongPaddleDirection direction) {
|
||||
if (Player1 == player) {
|
||||
State.Paddle1.Direction = direction;
|
||||
Logger.LogInformation(DIRECTION_LOG_TEMPLATE, ID, 1, player, direction);
|
||||
return;
|
||||
} else if (Player2 == player) {
|
||||
State.Paddle2.Direction = direction;
|
||||
Logger.LogInformation(DIRECTION_LOG_TEMPLATE, ID, 2, player, direction);
|
||||
return;
|
||||
}
|
||||
throw new InvalidOperationException("Player is not in this room, but moved! Assumably players room wasn't deleted.");
|
||||
}
|
||||
}
|
@ -6,5 +6,4 @@
|
||||
|
||||
<div class="text-center">
|
||||
<h1 class="display-4">Welcome</h1>
|
||||
<a asp-area="" asp-page="/Pong">Pong</a>
|
||||
</div>
|
||||
|
@ -9,7 +9,7 @@ public class IndexModel : PageModel {
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public IActionResult OnGet() {
|
||||
return RedirectToPage("Pong");
|
||||
public void OnGet() {
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -1,30 +0,0 @@
|
||||
@page
|
||||
@model PongModel
|
||||
@{
|
||||
ViewData["Title"] = "Pong";
|
||||
}
|
||||
|
||||
<div class="text-center">
|
||||
<h1 class="display-4">Pong</h1>
|
||||
<h3 id="connection">Connection Status</h3>
|
||||
|
||||
<button id="createlobby" class="btn btn-primary mb-3">Create</button>
|
||||
<button id="leavelobby" class="btn btn-primary mb-3">Leave</button>
|
||||
|
||||
<div class="input-group mb-3">
|
||||
<input id="roomid" type="text" class="form-control" placeholder="Room ID" aria-label="Room ID" aria-describedby="joinroom">
|
||||
<button id="joinroom" class="btn btn-outline-secondary" type="button">Button</button>
|
||||
</div>
|
||||
|
||||
<div class="input-group mb-3">
|
||||
<input id="username" type="text" class="form-control" placeholder="Username" aria-label="Username" aria-describedby="setusername">
|
||||
<button id="setusername" class="btn btn-outline-secondary" type="button">Set Username</button>
|
||||
</div>
|
||||
|
||||
<div id="canvas-container" class="mb-3"></div>
|
||||
</div>
|
||||
|
||||
<script src="~/lib/signalr/dist/browser/signalr.min.js"></script>
|
||||
<script src="~/lib/signalr/dist/browser/signalr-protocol-msgpack.min.js"></script>
|
||||
<script src="~/lib/pixi/dist/pixi.min.js"></script>
|
||||
<script src="~/js/pong.js"></script>
|
@ -1,15 +0,0 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
|
||||
namespace PongGame.Pages;
|
||||
public class PongModel : PageModel {
|
||||
private readonly ILogger<PongModel> _logger;
|
||||
|
||||
public PongModel(ILogger<PongModel> logger) {
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public void OnGet() {
|
||||
|
||||
}
|
||||
}
|
@ -8,7 +8,6 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.SignalR.Protocols.MessagePack" Version="6.0.10" />
|
||||
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.17.0" />
|
||||
</ItemGroup>
|
||||
|
||||
|
@ -1,14 +1,7 @@
|
||||
using MessagePack;
|
||||
using PongGame.Hubs;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
// Add services to the container.
|
||||
builder.Services.AddRazorPages();
|
||||
builder.Services.AddSingleton<PongLobby>(services
|
||||
=> new(services.GetRequiredService<ILogger<PongLobby>>()));
|
||||
builder.Services.AddSignalR()
|
||||
.AddMessagePackProtocol();
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
@ -22,9 +15,6 @@ app.UseRouting();
|
||||
|
||||
app.UseAuthorization();
|
||||
|
||||
app.UseEndpoints(endpoints => {
|
||||
endpoints.MapRazorPages();
|
||||
endpoints.MapHub<PongHub>("/pong/hub");
|
||||
});
|
||||
app.MapRazorPages();
|
||||
|
||||
app.Run();
|
||||
|
@ -1,37 +0,0 @@
|
||||
{
|
||||
"version": "1.0",
|
||||
"defaultProvider": "jsdelivr",
|
||||
"libraries": [
|
||||
{
|
||||
"library": "@microsoft/signalr-protocol-msgpack@6.0.10",
|
||||
"destination": "wwwroot/lib/signalr/",
|
||||
"files": [
|
||||
"dist/browser/signalr-protocol-msgpack.js",
|
||||
"dist/browser/signalr-protocol-msgpack.js.map",
|
||||
"dist/browser/signalr-protocol-msgpack.min.js",
|
||||
"dist/browser/signalr-protocol-msgpack.min.js.map"
|
||||
]
|
||||
},
|
||||
{
|
||||
"library": "@microsoft/signalr@6.0.10",
|
||||
"destination": "wwwroot/lib/signalr/",
|
||||
"files": [
|
||||
"dist/browser/signalr.js",
|
||||
"dist/browser/signalr.js.map",
|
||||
"dist/browser/signalr.min.js",
|
||||
"dist/browser/signalr.min.js.map"
|
||||
]
|
||||
},
|
||||
{
|
||||
"provider": "jsdelivr",
|
||||
"library": "pixi.js@7.0.2",
|
||||
"destination": "wwwroot/lib/pixi/",
|
||||
"files": [
|
||||
"dist/pixi.js",
|
||||
"dist/pixi.js.map",
|
||||
"dist/pixi.min.js.map",
|
||||
"dist/pixi.min.js"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
@ -1,169 +0,0 @@
|
||||
"use strict";
|
||||
|
||||
const connection = new signalR.HubConnectionBuilder()
|
||||
.withUrl("/pong/hub")
|
||||
.withAutomaticReconnect()
|
||||
.withHubProtocol(new signalR.protocols.msgpack.MessagePackHubProtocol())
|
||||
.build();
|
||||
|
||||
function getElement(id) {
|
||||
return document.getElementById(id) ?? console.error(`Element #${id} not found!`)
|
||||
}
|
||||
|
||||
const connectionStatus = getElement("connection");
|
||||
const createlobby = getElement("createlobby");
|
||||
const roomidinput = getElement("roomid");
|
||||
const joinroom = getElement("joinroom");
|
||||
const usernameinput = getElement("username");
|
||||
const setusername = getElement("setusername");
|
||||
const leavelobby = getElement("leavelobby");
|
||||
|
||||
connection.onclose(function (error) {
|
||||
if (error) {
|
||||
connectionStatus.textContent = "Unexpected error!";
|
||||
return console.error(`Connection aborted: ${error.message}`);
|
||||
}
|
||||
console.info("Disconnected!");
|
||||
connectionStatus.textContent = "Closed!";
|
||||
});
|
||||
|
||||
connection.onreconnecting(function (error) {
|
||||
if (error) {
|
||||
connectionStatus.textContent = "Reconnecting!";
|
||||
return console.error(`Connection reconnecting: ${error.message}`);
|
||||
}
|
||||
console.info("Reconnecting!");
|
||||
connectionStatus.textContent = "Reconnecting!";
|
||||
});
|
||||
|
||||
connection.onreconnected(function (connectionId) {
|
||||
console.info(`Connected as ${connectionId}!`);
|
||||
connectionStatus.textContent = "Connected!";
|
||||
});
|
||||
|
||||
createlobby.addEventListener("click", function (event) {
|
||||
connection.invoke("CreateRoom").then(function (roomId) {
|
||||
roomidinput.value = roomId;
|
||||
console.info(`Joined room [${roomId}]`);
|
||||
}).catch(function (err) {
|
||||
return console.error(err.toString());
|
||||
});
|
||||
event.preventDefault();
|
||||
});
|
||||
|
||||
connection.on("GameStateChanged", function (state) {
|
||||
console.info(`Game is now in state ${state}`);
|
||||
});
|
||||
connection.on("UsernameChanged", function (username) {
|
||||
console.info(`Username is now ${username}`);
|
||||
usernameinput.value = username;
|
||||
});
|
||||
|
||||
joinroom.addEventListener("click", function (event) {
|
||||
connection.invoke("JoinRoom", roomidinput.value).catch(function (err) {
|
||||
return console.error(err.toString());
|
||||
});
|
||||
event.preventDefault();
|
||||
});
|
||||
|
||||
setusername.addEventListener("click", function (event) {
|
||||
connection.invoke("RequestUsernameChange", usernameinput.value).catch(function (err) {
|
||||
return console.error(err.toString());
|
||||
});
|
||||
event.preventDefault();
|
||||
});
|
||||
|
||||
leavelobby.addEventListener("click", function (event) {
|
||||
connection.invoke("LeaveRoom").catch(function (err) {
|
||||
return console.error(err.toString());
|
||||
});
|
||||
event.preventDefault();
|
||||
});
|
||||
|
||||
function movePaddle(direction) {
|
||||
connection.invoke("MovePaddle", direction).catch(function (err) {
|
||||
return console.error(err.toString());
|
||||
});
|
||||
}
|
||||
|
||||
function moveUp() { return movePaddle(-1); }
|
||||
function stopPaddle() { return movePaddle(0); }
|
||||
function moveDown() { return movePaddle(1); }
|
||||
|
||||
// Create the application helper and add its render target to the page
|
||||
let app = new PIXI.Application({ width: 1000, height: 500 });
|
||||
getElement('canvas-container').appendChild(app.view);
|
||||
|
||||
let graphics = new PIXI.Graphics();
|
||||
app.stage.addChild(graphics);
|
||||
|
||||
function renderPaddle(graphics, state, xMid) {
|
||||
var xLeft = xMid - 5;
|
||||
graphics.beginFill(0x00FFFF);
|
||||
graphics.drawRect(xLeft, state.Height - 25, 10, 50);
|
||||
graphics.endFill();
|
||||
}
|
||||
|
||||
function renderBall(graphics, state) {
|
||||
graphics.beginFill(0xFF00FF);
|
||||
graphics.drawCircle(state.Pos.X, state.Pos.Y, 4);
|
||||
graphics.endFill();
|
||||
}
|
||||
|
||||
function renderGameState(graphics, state) {
|
||||
graphics.clear();
|
||||
renderPaddle(graphics, state.Paddle1, 50);
|
||||
renderPaddle(graphics, state.Paddle2, 1000 - 50);
|
||||
renderBall(graphics, state.BallState);
|
||||
}
|
||||
|
||||
connection.on("ReceiveGameState", function (state) {
|
||||
renderGameState(graphics, state);
|
||||
});
|
||||
|
||||
const keyEvent = (function () {
|
||||
var upPressed = false;
|
||||
var downPressed = false;
|
||||
|
||||
function moveUpdated() {
|
||||
if (upPressed == downPressed) stopPaddle();
|
||||
else if (upPressed) moveUp();
|
||||
else if (downPressed) moveDown();
|
||||
else console.error("unknown move!");
|
||||
}
|
||||
|
||||
function handler(event) {
|
||||
if (event.repeat) return;
|
||||
if (event.path.indexOf(document.body) != 0) return; // only use key if it was pressed on empty space
|
||||
var pressed = event.type == "keyup";
|
||||
// W Key is 87, Up arrow is 87
|
||||
// S Key is 83, Down arrow is 40
|
||||
switch (event.keyCode) {
|
||||
case 87:
|
||||
case 38:
|
||||
upPressed = pressed;
|
||||
break;
|
||||
case 83:
|
||||
case 40:
|
||||
downPressed = pressed;
|
||||
break;
|
||||
default: return;
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
moveUpdated();
|
||||
}
|
||||
|
||||
return handler;
|
||||
})();
|
||||
|
||||
document.addEventListener('keydown', keyEvent);
|
||||
document.addEventListener('keyup', keyEvent);
|
||||
|
||||
connection.start().then(function () {
|
||||
console.info(`Connected!`);
|
||||
connectionStatus.textContent = "Connected!";
|
||||
}).catch(function (err) {
|
||||
connectionStatus.textContent = "Connection failed!";
|
||||
return console.error(err.toString());
|
||||
});
|
24183
PongGame/wwwroot/lib/pixi/dist/pixi.js
vendored
24183
PongGame/wwwroot/lib/pixi/dist/pixi.js
vendored
File diff suppressed because it is too large
Load Diff
1
PongGame/wwwroot/lib/pixi/dist/pixi.js.map
vendored
1
PongGame/wwwroot/lib/pixi/dist/pixi.js.map
vendored
File diff suppressed because one or more lines are too long
1116
PongGame/wwwroot/lib/pixi/dist/pixi.min.js
vendored
1116
PongGame/wwwroot/lib/pixi/dist/pixi.min.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
3115
PongGame/wwwroot/lib/signalr/dist/browser/signalr.js
vendored
3115
PongGame/wwwroot/lib/signalr/dist/browser/signalr.js
vendored
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
324
package-lock.json
generated
324
package-lock.json
generated
@ -1,324 +0,0 @@
|
||||
{
|
||||
"name": "PongGame",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"dependencies": {
|
||||
"@microsoft/signalr": "^6.0.10",
|
||||
"@microsoft/signalr-protocol-msgpack": "^6.0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/@microsoft/signalr": {
|
||||
"version": "6.0.10",
|
||||
"resolved": "https://registry.npmjs.org/@microsoft/signalr/-/signalr-6.0.10.tgz",
|
||||
"integrity": "sha512-ND9LiIYac+ZDgCgW2QzpNfe9BTiOtjc2AX/2GtFIhRGhEzx5CixcNANg2VGj27IAxycAPPnEoy7+QA31Eil7QQ==",
|
||||
"dependencies": {
|
||||
"abort-controller": "^3.0.0",
|
||||
"eventsource": "^1.0.7",
|
||||
"fetch-cookie": "^0.11.0",
|
||||
"node-fetch": "^2.6.7",
|
||||
"ws": "^7.4.5"
|
||||
}
|
||||
},
|
||||
"node_modules/@microsoft/signalr-protocol-msgpack": {
|
||||
"version": "6.0.10",
|
||||
"resolved": "https://registry.npmjs.org/@microsoft/signalr-protocol-msgpack/-/signalr-protocol-msgpack-6.0.10.tgz",
|
||||
"integrity": "sha512-Xj3QuH/HMcLGtc+iZjE8BH/auQVn4FQY9adv7M/wsMpT9TEe1iQJKjD0Hlh+Y42ioNaO0MFVlfFXhkYUK7y5QQ==",
|
||||
"dependencies": {
|
||||
"@microsoft/signalr": ">=6.0.10",
|
||||
"@msgpack/msgpack": "^2.7.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@msgpack/msgpack": {
|
||||
"version": "2.8.0",
|
||||
"resolved": "https://registry.npmjs.org/@msgpack/msgpack/-/msgpack-2.8.0.tgz",
|
||||
"integrity": "sha512-h9u4u/jiIRKbq25PM+zymTyW6bhTzELvOoUd+AvYriWOAKpLGnIamaET3pnHYoI5iYphAHBI4ayx0MehR+VVPQ==",
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/abort-controller": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz",
|
||||
"integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==",
|
||||
"dependencies": {
|
||||
"event-target-shim": "^5.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.5"
|
||||
}
|
||||
},
|
||||
"node_modules/event-target-shim": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz",
|
||||
"integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/eventsource": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/eventsource/-/eventsource-1.1.2.tgz",
|
||||
"integrity": "sha512-xAH3zWhgO2/3KIniEKYPr8plNSzlGINOUqYj0m0u7AB81iRw8b/3E73W6AuU+6klLbaSFmZnaETQ2lXPfAydrA==",
|
||||
"engines": {
|
||||
"node": ">=0.12.0"
|
||||
}
|
||||
},
|
||||
"node_modules/fetch-cookie": {
|
||||
"version": "0.11.0",
|
||||
"resolved": "https://registry.npmjs.org/fetch-cookie/-/fetch-cookie-0.11.0.tgz",
|
||||
"integrity": "sha512-BQm7iZLFhMWFy5CZ/162sAGjBfdNWb7a8LEqqnzsHFhxT/X/SVj/z2t2nu3aJvjlbQkrAlTUApplPRjWyH4mhA==",
|
||||
"dependencies": {
|
||||
"tough-cookie": "^2.3.3 || ^3.0.1 || ^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/node-fetch": {
|
||||
"version": "2.6.7",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz",
|
||||
"integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==",
|
||||
"dependencies": {
|
||||
"whatwg-url": "^5.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "4.x || >=6.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"encoding": "^0.1.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"encoding": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/psl": {
|
||||
"version": "1.9.0",
|
||||
"resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz",
|
||||
"integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag=="
|
||||
},
|
||||
"node_modules/punycode": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
|
||||
"integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/querystringify": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz",
|
||||
"integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ=="
|
||||
},
|
||||
"node_modules/requires-port": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
|
||||
"integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ=="
|
||||
},
|
||||
"node_modules/tough-cookie": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.2.tgz",
|
||||
"integrity": "sha512-G9fqXWoYFZgTc2z8Q5zaHy/vJMjm+WV0AkAeHxVCQiEB1b+dGvWzFW6QV07cY5jQ5gRkeid2qIkzkxUnmoQZUQ==",
|
||||
"dependencies": {
|
||||
"psl": "^1.1.33",
|
||||
"punycode": "^2.1.1",
|
||||
"universalify": "^0.2.0",
|
||||
"url-parse": "^1.5.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/tr46": {
|
||||
"version": "0.0.3",
|
||||
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
|
||||
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
|
||||
},
|
||||
"node_modules/universalify": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz",
|
||||
"integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==",
|
||||
"engines": {
|
||||
"node": ">= 4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/url-parse": {
|
||||
"version": "1.5.10",
|
||||
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz",
|
||||
"integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==",
|
||||
"dependencies": {
|
||||
"querystringify": "^2.1.1",
|
||||
"requires-port": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/webidl-conversions": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
|
||||
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
|
||||
},
|
||||
"node_modules/whatwg-url": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
|
||||
"integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
|
||||
"dependencies": {
|
||||
"tr46": "~0.0.3",
|
||||
"webidl-conversions": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/ws": {
|
||||
"version": "7.5.9",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz",
|
||||
"integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==",
|
||||
"engines": {
|
||||
"node": ">=8.3.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"bufferutil": "^4.0.1",
|
||||
"utf-8-validate": "^5.0.2"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"bufferutil": {
|
||||
"optional": true
|
||||
},
|
||||
"utf-8-validate": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@microsoft/signalr": {
|
||||
"version": "6.0.10",
|
||||
"resolved": "https://registry.npmjs.org/@microsoft/signalr/-/signalr-6.0.10.tgz",
|
||||
"integrity": "sha512-ND9LiIYac+ZDgCgW2QzpNfe9BTiOtjc2AX/2GtFIhRGhEzx5CixcNANg2VGj27IAxycAPPnEoy7+QA31Eil7QQ==",
|
||||
"requires": {
|
||||
"abort-controller": "^3.0.0",
|
||||
"eventsource": "^1.0.7",
|
||||
"fetch-cookie": "^0.11.0",
|
||||
"node-fetch": "^2.6.7",
|
||||
"ws": "^7.4.5"
|
||||
}
|
||||
},
|
||||
"@microsoft/signalr-protocol-msgpack": {
|
||||
"version": "6.0.10",
|
||||
"resolved": "https://registry.npmjs.org/@microsoft/signalr-protocol-msgpack/-/signalr-protocol-msgpack-6.0.10.tgz",
|
||||
"integrity": "sha512-Xj3QuH/HMcLGtc+iZjE8BH/auQVn4FQY9adv7M/wsMpT9TEe1iQJKjD0Hlh+Y42ioNaO0MFVlfFXhkYUK7y5QQ==",
|
||||
"requires": {
|
||||
"@microsoft/signalr": ">=6.0.10",
|
||||
"@msgpack/msgpack": "^2.7.0"
|
||||
}
|
||||
},
|
||||
"@msgpack/msgpack": {
|
||||
"version": "2.8.0",
|
||||
"resolved": "https://registry.npmjs.org/@msgpack/msgpack/-/msgpack-2.8.0.tgz",
|
||||
"integrity": "sha512-h9u4u/jiIRKbq25PM+zymTyW6bhTzELvOoUd+AvYriWOAKpLGnIamaET3pnHYoI5iYphAHBI4ayx0MehR+VVPQ=="
|
||||
},
|
||||
"abort-controller": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz",
|
||||
"integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==",
|
||||
"requires": {
|
||||
"event-target-shim": "^5.0.0"
|
||||
}
|
||||
},
|
||||
"event-target-shim": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz",
|
||||
"integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ=="
|
||||
},
|
||||
"eventsource": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/eventsource/-/eventsource-1.1.2.tgz",
|
||||
"integrity": "sha512-xAH3zWhgO2/3KIniEKYPr8plNSzlGINOUqYj0m0u7AB81iRw8b/3E73W6AuU+6klLbaSFmZnaETQ2lXPfAydrA=="
|
||||
},
|
||||
"fetch-cookie": {
|
||||
"version": "0.11.0",
|
||||
"resolved": "https://registry.npmjs.org/fetch-cookie/-/fetch-cookie-0.11.0.tgz",
|
||||
"integrity": "sha512-BQm7iZLFhMWFy5CZ/162sAGjBfdNWb7a8LEqqnzsHFhxT/X/SVj/z2t2nu3aJvjlbQkrAlTUApplPRjWyH4mhA==",
|
||||
"requires": {
|
||||
"tough-cookie": "^2.3.3 || ^3.0.1 || ^4.0.0"
|
||||
}
|
||||
},
|
||||
"node-fetch": {
|
||||
"version": "2.6.7",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz",
|
||||
"integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==",
|
||||
"requires": {
|
||||
"whatwg-url": "^5.0.0"
|
||||
}
|
||||
},
|
||||
"psl": {
|
||||
"version": "1.9.0",
|
||||
"resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz",
|
||||
"integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag=="
|
||||
},
|
||||
"punycode": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
|
||||
"integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A=="
|
||||
},
|
||||
"querystringify": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz",
|
||||
"integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ=="
|
||||
},
|
||||
"requires-port": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
|
||||
"integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ=="
|
||||
},
|
||||
"tough-cookie": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.2.tgz",
|
||||
"integrity": "sha512-G9fqXWoYFZgTc2z8Q5zaHy/vJMjm+WV0AkAeHxVCQiEB1b+dGvWzFW6QV07cY5jQ5gRkeid2qIkzkxUnmoQZUQ==",
|
||||
"requires": {
|
||||
"psl": "^1.1.33",
|
||||
"punycode": "^2.1.1",
|
||||
"universalify": "^0.2.0",
|
||||
"url-parse": "^1.5.3"
|
||||
}
|
||||
},
|
||||
"tr46": {
|
||||
"version": "0.0.3",
|
||||
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
|
||||
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
|
||||
},
|
||||
"universalify": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz",
|
||||
"integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg=="
|
||||
},
|
||||
"url-parse": {
|
||||
"version": "1.5.10",
|
||||
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz",
|
||||
"integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==",
|
||||
"requires": {
|
||||
"querystringify": "^2.1.1",
|
||||
"requires-port": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"webidl-conversions": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
|
||||
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
|
||||
},
|
||||
"whatwg-url": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
|
||||
"integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
|
||||
"requires": {
|
||||
"tr46": "~0.0.3",
|
||||
"webidl-conversions": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"ws": {
|
||||
"version": "7.5.9",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz",
|
||||
"integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==",
|
||||
"requires": {}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"@microsoft/signalr": "^6.0.10",
|
||||
"@microsoft/signalr-protocol-msgpack": "^6.0.10"
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user