From 01ff7422078eb91f770c1fe7205db3e6bc6a1f0d Mon Sep 17 00:00:00 2001 From: Hamcha Date: Mon, 23 Sep 2019 12:50:44 +0200 Subject: [PATCH 01/18] Add forms for hosting/joining sessions --- src/mlpccg/set.ts | 17 +++++ src/views/Lobby.vue | 177 +++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 193 insertions(+), 1 deletion(-) diff --git a/src/mlpccg/set.ts b/src/mlpccg/set.ts index c554c8f..2435338 100644 --- a/src/mlpccg/set.ts +++ b/src/mlpccg/set.ts @@ -19,6 +19,23 @@ export const allSets = [ "Promo" ]; +export const setNames = { + PR: "Premiere", + CN: "Canterlot Nights", + RR: "Rock and Rave", + CS: "Celestial Solstice", + CG: "Crystal Games", + AD: "Absolute Discord", + EO: "Equestrian Odysseys", + HM: "High Magic", + MT: "Marks In Time", + DE: "Defenders of Equestria", + SB: "Seaquestria and Beyond", + FF: "Friends Forever", + GF: "Promotional", + ST: "Sands of Time" +}; + export async function loadSets() { if (Database == null) { throw new Error("Database was not initialized, init with 'initDB()'"); diff --git a/src/views/Lobby.vue b/src/views/Lobby.vue index 041677a..5a382f3 100644 --- a/src/views/Lobby.vue +++ b/src/views/Lobby.vue @@ -1,26 +1,201 @@ -- 2.40.1 From 4306045991dcbef3b634e5490837adacd7e30d41 Mon Sep 17 00:00:00 2001 From: Hamcha Date: Mon, 23 Sep 2019 15:10:26 +0200 Subject: [PATCH 02/18] Add player name --- src/views/Lobby.vue | 84 +++++++++++++++++++++++++++++++++++---------- 1 file changed, 66 insertions(+), 18 deletions(-) diff --git a/src/views/Lobby.vue b/src/views/Lobby.vue index 5a382f3..3e27b6c 100644 --- a/src/views/Lobby.vue +++ b/src/views/Lobby.vue @@ -2,7 +2,30 @@
-
+
+ + + +
+
+
+

Join someone's session

+
+ + + +
+ + Join + +
+
+

Host a new session

@@ -49,24 +72,16 @@
- + Create
-
-
-

Join someone's session

-
- - - -
- - Join - -
-
@@ -85,16 +100,33 @@ .body { flex: 1; - display: flex; + display: grid; padding: 10px; padding-top: 0; + grid-template: 150px 1fr / 1fr 1fr; + + #info { + grid-row: 1; + grid-column: 1 / end; + } + + #join, + #host { + grid-row: 2; + } + + #join { + grid-column: 2; + } + #host { + grid-column: 1; + } section { margin: 10px; border: 1px solid rgba($white, 20%); border-radius: 10px; padding: 20px; - flex: 1; header { font-family: $fantasy; h1 { @@ -125,13 +157,19 @@ @media (max-width: 500px) { .body { + grid-template: 120px 1fr 1fr /1fr; + #info, + #join, + #host { + grid-row: auto; + grid-column: auto; + } section { padding: 10px; header h1 { font-size: 14pt; } } - flex-flow: column-reverse; } } @@ -147,6 +185,7 @@ import { allSets, setNames } from "../mlpccg"; } }) export default class Lobby extends Vue { + private playerName!: string; private hostType!: string; private hostDraftType!: string; private hostDraftSet!: string; @@ -157,6 +196,11 @@ export default class Lobby extends Vue { private data() { return { + playerName: + "Guest-" + + Math.random() + .toString() + .slice(2, 8), hostType: "game", hostDraftType: "set", hostDraftSet: "FF", @@ -167,6 +211,10 @@ export default class Lobby extends Vue { }; } + private async create() {} + + private async join() {} + private get canHost(): boolean { switch (this.hostType) { case "game": -- 2.40.1 From e67f49fb0613c2733ce5a5638dcfa1b6c99637a2 Mon Sep 17 00:00:00 2001 From: Hamcha Date: Mon, 23 Sep 2019 16:18:55 +0200 Subject: [PATCH 03/18] Better mobile support --- src/views/Lobby.vue | 160 ++++++++++++++++++-------------------------- 1 file changed, 64 insertions(+), 96 deletions(-) diff --git a/src/views/Lobby.vue b/src/views/Lobby.vue index 3e27b6c..381d565 100644 --- a/src/views/Lobby.vue +++ b/src/views/Lobby.vue @@ -11,10 +11,10 @@

Join someone's session

- + -
+
+ + +

+ + Join + +

+

Host a new session

- - - - Game - - - Draft - - -
- -
-
- - - - - - - - - - - - - - - +
+
+ + + +
+
+ + + +
+
- + Create
@@ -143,6 +120,11 @@ margin: 10px auto; } +.full { + width: 100%; + margin: 10px; +} + .center { width: 100%; text-align: center; @@ -155,15 +137,20 @@ } } +.only-mobile { + display: none; +} + @media (max-width: 500px) { + .only-full { + display: none; + } + .only-mobile { + display: inherit; + } .body { - grid-template: 120px 1fr 1fr /1fr; - #info, - #join, - #host { - grid-row: auto; - grid-column: auto; - } + display: flex; + flex-direction: column; section { padding: 10px; header h1 { @@ -177,7 +164,8 @@ -- 2.40.1 From 94b361c21f3ac27ff8cd135406772810a1202097 Mon Sep 17 00:00:00 2001 From: Hamcha Date: Mon, 23 Sep 2019 17:58:48 +0200 Subject: [PATCH 08/18] Is Vuex broken?! --- src/store/network/actions.ts | 4 ++-- src/store/network/mutations.ts | 15 ++++++++------- src/store/network/types.ts | 8 +++++++- 3 files changed, 17 insertions(+), 10 deletions(-) diff --git a/src/store/network/actions.ts b/src/store/network/actions.ts index dfc9f4b..7c83918 100644 --- a/src/store/network/actions.ts +++ b/src/store/network/actions.ts @@ -14,10 +14,10 @@ const actions: ActionTree = { const client = new PeerClient(options.playerInfo, options._customPeer); commit("becomeClient", { peer: client }); client.on("connected", () => { - commit("connected"); + commit("connectionStatusChanged", "connected"); }); client.on("disconnected", () => { - commit("disconnected"); + commit("connectionStatusChanged", "disconnected"); }); client.on("error", err => { commit("connectionError", err); diff --git a/src/store/network/mutations.ts b/src/store/network/mutations.ts index c6decfe..bc83413 100644 --- a/src/store/network/mutations.ts +++ b/src/store/network/mutations.ts @@ -1,5 +1,10 @@ import { MutationTree } from "vuex"; -import { NetworkState, ServerNetworkState, ClientNetworkState } from "./types"; +import { + NetworkState, + ServerNetworkState, + ClientNetworkState, + ConnectionStatus +} from "./types"; import { LocalClient, PeerServer, PeerClient } from "@/network"; const mutations: MutationTree = { @@ -15,12 +20,8 @@ const mutations: MutationTree = { (state as ClientNetworkState).peer = payload.peer; }, - connected(state) { - (state as ClientNetworkState).connectionStatus = "connected"; - }, - - disconnected(state) { - (state as ClientNetworkState).connectionStatus = "disconnected"; + connectionStatusChanged(state, status: ConnectionStatus) { + (state as ClientNetworkState).connectionStatus = status; }, connectionError(state, error) { diff --git a/src/store/network/types.ts b/src/store/network/types.ts index 4c8e555..54adbaf 100644 --- a/src/store/network/types.ts +++ b/src/store/network/types.ts @@ -7,6 +7,12 @@ import { } from "@/network"; import Peer from "peerjs"; +export type ConnectionStatus = + | "connecting" + | "connected" + | "disconnected" + | "error"; + export interface ChatMessage { who: string; to: string; @@ -23,7 +29,7 @@ export interface NoNetworkState extends SharedNetworkState { export interface ClientNetworkState extends SharedNetworkState { peerType: "client"; - connectionStatus: "connecting" | "connected" | "disconnected" | "error"; + connectionStatus: ConnectionStatus; connectionError?: Error; peer: PeerClient; } -- 2.40.1 From c30b4c46e00ab16edfb46ec047dc4c9a62627f01 Mon Sep 17 00:00:00 2001 From: Hamcha Date: Mon, 23 Sep 2019 18:05:26 +0200 Subject: [PATCH 09/18] nvm fixed it --- src/store/network/index.ts | 2 ++ src/store/network/types.ts | 3 +++ 2 files changed, 5 insertions(+) diff --git a/src/store/network/index.ts b/src/store/network/index.ts index 9a0ca12..0eaf023 100644 --- a/src/store/network/index.ts +++ b/src/store/network/index.ts @@ -10,6 +10,8 @@ const namespaced = true; export const state: NetworkState = { peerType: "none", + connectionStatus: null, + peer: null, chatLog: [] }; diff --git a/src/store/network/types.ts b/src/store/network/types.ts index 54adbaf..6b85cb8 100644 --- a/src/store/network/types.ts +++ b/src/store/network/types.ts @@ -25,6 +25,9 @@ export interface SharedNetworkState { export interface NoNetworkState extends SharedNetworkState { peerType: "none"; + connectionStatus: null; + connectionError?: Error; + peer: null; } export interface ClientNetworkState extends SharedNetworkState { -- 2.40.1 From ad4d654abdec793da83a923d0f863ccac8a559db Mon Sep 17 00:00:00 2001 From: Hamcha Date: Tue, 24 Sep 2019 09:53:49 +0200 Subject: [PATCH 10/18] Add Session ID --- src/store/network/actions.ts | 2 +- src/store/network/getters.ts | 10 ++++++++++ src/store/network/index.ts | 3 +++ src/store/network/mutations.ts | 3 ++- src/store/network/types.ts | 4 ++++ src/views/Lobby.vue | 7 ++++++- 6 files changed, 26 insertions(+), 3 deletions(-) diff --git a/src/store/network/actions.ts b/src/store/network/actions.ts index 7c83918..4a4de2f 100644 --- a/src/store/network/actions.ts +++ b/src/store/network/actions.ts @@ -12,7 +12,7 @@ const actions: ActionTree = { connect({ commit }, options: ConnectOptions) { const client = new PeerClient(options.playerInfo, options._customPeer); - commit("becomeClient", { peer: client }); + commit("becomeClient", { peer: client, id: options.serverID }); client.on("connected", () => { commit("connectionStatusChanged", "connected"); }); diff --git a/src/store/network/getters.ts b/src/store/network/getters.ts index de91bd9..66454e6 100644 --- a/src/store/network/getters.ts +++ b/src/store/network/getters.ts @@ -13,6 +13,16 @@ const getters: GetterTree = { return null; }, + sessionID(state): string | null { + switch (state.peerType) { + case "server": + return state.server.id; + case "client": + return state.serverID; + } + return null; + }, + connectionType(state): "client" | "server" | "none" { return state.peerType; }, diff --git a/src/store/network/index.ts b/src/store/network/index.ts index 0eaf023..9c43e77 100644 --- a/src/store/network/index.ts +++ b/src/store/network/index.ts @@ -12,6 +12,9 @@ export const state: NetworkState = { peerType: "none", connectionStatus: null, peer: null, + server: null, + local: null, + serverID: null, chatLog: [] }; diff --git a/src/store/network/mutations.ts b/src/store/network/mutations.ts index bc83413..7d8e0bb 100644 --- a/src/store/network/mutations.ts +++ b/src/store/network/mutations.ts @@ -14,10 +14,11 @@ const mutations: MutationTree = { (state as ServerNetworkState).server = payload.server; }, - becomeClient(state, payload: { peer: PeerClient }) { + becomeClient(state, payload: { peer: PeerClient; id: string }) { state.peerType = "client"; (state as ClientNetworkState).connectionStatus = "connecting"; (state as ClientNetworkState).peer = payload.peer; + (state as ClientNetworkState).serverID = payload.id; }, connectionStatusChanged(state, status: ConnectionStatus) { diff --git a/src/store/network/types.ts b/src/store/network/types.ts index 6b85cb8..c969aa2 100644 --- a/src/store/network/types.ts +++ b/src/store/network/types.ts @@ -28,6 +28,9 @@ export interface NoNetworkState extends SharedNetworkState { connectionStatus: null; connectionError?: Error; peer: null; + server: null; + local: null; + serverID: null; } export interface ClientNetworkState extends SharedNetworkState { @@ -35,6 +38,7 @@ export interface ClientNetworkState extends SharedNetworkState { connectionStatus: ConnectionStatus; connectionError?: Error; peer: PeerClient; + serverID: string; } export interface ServerNetworkState extends SharedNetworkState { diff --git a/src/views/Lobby.vue b/src/views/Lobby.vue index bc7c769..03abd04 100644 --- a/src/views/Lobby.vue +++ b/src/views/Lobby.vue @@ -79,7 +79,9 @@
-
+
+ Session ID: {{ sessionID }} +
@@ -209,6 +211,9 @@ export default class Lobby extends Vue { @Getter("inRoom", { namespace: "network" }) private inRoom!: boolean; + @Getter("sessionID", { namespace: "network"} ) + private sessionID!: string | null; + private data() { return { playerName: -- 2.40.1 From 9b9904adbd0b1e0f698ceac458490a798597f28a Mon Sep 17 00:00:00 2001 From: Hamcha Date: Wed, 2 Oct 2019 17:53:27 +0200 Subject: [PATCH 11/18] Add vuex actions/getters for chat messages --- src/store/network/actions.ts | 24 +++++++++++++++++++++++- src/store/network/getters.ts | 11 +++++++++++ src/store/network/mutations.ts | 6 +++++- src/store/network/types.ts | 9 ++------- 4 files changed, 41 insertions(+), 9 deletions(-) diff --git a/src/store/network/actions.ts b/src/store/network/actions.ts index 4a4de2f..b48570d 100644 --- a/src/store/network/actions.ts +++ b/src/store/network/actions.ts @@ -1,7 +1,14 @@ import { ActionTree } from "vuex"; import { AppState } from "../types"; import { NetworkState, StartServerOptions, ConnectOptions } from "./types"; -import { PeerServer, LocalClient, PeerClient } from "@/network"; +import { + PeerServer, + LocalClient, + PeerClient, + NetworkMessage, + Client, + ChatMessage +} from "@/network"; const actions: ActionTree = { startServer({ commit }, options: StartServerOptions) { @@ -23,6 +30,21 @@ const actions: ActionTree = { commit("connectionError", err); }); client.connect(options.serverID); + }, + + sendChatMessage({ commit, dispatch, getters }, message: ChatMessage) { + if (getters.connectionType == "none") { + throw new Error("not connected"); + } + dispatch("sendMessage", message); + commit("receivedChatMessage", message); + }, + + sendMessage({ getters }, message: NetworkMessage) { + if (getters.connectionType == "none") { + throw new Error("not connected"); + } + (getters.client as Client).send(message); } }; diff --git a/src/store/network/getters.ts b/src/store/network/getters.ts index 66454e6..d69b772 100644 --- a/src/store/network/getters.ts +++ b/src/store/network/getters.ts @@ -1,6 +1,7 @@ import { GetterTree } from "vuex"; import { AppState } from "../types"; import { NetworkState } from "./types"; +import { Client } from "@/network"; const getters: GetterTree = { peerID(state): string | null { @@ -23,6 +24,16 @@ const getters: GetterTree = { return null; }, + client(state): Client | null { + switch (state.peerType) { + case "server": + return state.local; + case "client": + return state.peer; + } + return null; + }, + connectionType(state): "client" | "server" | "none" { return state.peerType; }, diff --git a/src/store/network/mutations.ts b/src/store/network/mutations.ts index 7d8e0bb..dae7c3d 100644 --- a/src/store/network/mutations.ts +++ b/src/store/network/mutations.ts @@ -5,7 +5,7 @@ import { ClientNetworkState, ConnectionStatus } from "./types"; -import { LocalClient, PeerServer, PeerClient } from "@/network"; +import { LocalClient, PeerServer, PeerClient, ChatMessage } from "@/network"; const mutations: MutationTree = { becomeServer(state, payload: { local: LocalClient; server: PeerServer }) { @@ -28,6 +28,10 @@ const mutations: MutationTree = { connectionError(state, error) { (state as ClientNetworkState).connectionStatus = "error"; (state as ClientNetworkState).connectionError = error; + }, + + receivedChatMessage(state, message: ChatMessage) { + state.chatLog.push(message); } }; diff --git a/src/store/network/types.ts b/src/store/network/types.ts index c969aa2..5d868eb 100644 --- a/src/store/network/types.ts +++ b/src/store/network/types.ts @@ -3,7 +3,8 @@ import { PeerServer, LocalClient, RoomInfo, - PeerMetadata + PeerMetadata, + ChatMessage } from "@/network"; import Peer from "peerjs"; @@ -13,12 +14,6 @@ export type ConnectionStatus = | "disconnected" | "error"; -export interface ChatMessage { - who: string; - to: string; - message: string; -} - export interface SharedNetworkState { chatLog: ChatMessage[]; } -- 2.40.1 From 6b34fe3dfa8e5350705d08ce8c1fd4c865349999 Mon Sep 17 00:00:00 2001 From: Hamcha Date: Mon, 7 Oct 2019 12:22:40 +0200 Subject: [PATCH 12/18] Wait for connection and properly clean up players --- src/network/PeerServer.ts | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/src/network/PeerServer.ts b/src/network/PeerServer.ts index e88ffbf..67e3226 100644 --- a/src/network/PeerServer.ts +++ b/src/network/PeerServer.ts @@ -34,6 +34,12 @@ function nextName(name: string): string { return name.substr(0, i) + (Number(name.slice(i)) + 1); } +function connectionOpen(conn: DataConnection): Promise { + return new Promise(resolve => { + conn.on("open", () => resolve()); + }); +} + export class PeerServer extends EventEmitter { protected peer: Peer; private room: Room; @@ -64,17 +70,21 @@ export class PeerServer extends EventEmitter { // Setup peer this.peer = customPeer ? customPeer : new Peer(); - this.peer.on("open", function(id) { + this.peer.on("open", id => { console.info("Peer ID assigned: %s", id); + this.emit("open", id); }); this.peer.on("connection", conn => { this._connection(conn); }); } - private _connection(conn: DataConnection) { + private async _connection(conn: DataConnection) { const metadata = conn.metadata as PeerMetadata; + // Wait for connection to be open + await connectionOpen(conn); + let player: NetworkPlayer = { kind: "remote", name: metadata.name, @@ -140,6 +150,12 @@ export class PeerServer extends EventEmitter { private addPlayer(player: NetworkPlayer) { const playerName = player.name; + + // Hacky: Give player list before this player was added, so join + // message doesn't mess things up later + const players = Object.keys(this.room.players); + + // Add player to room this.room.players[playerName] = player; // Start listening for new messages @@ -148,6 +164,10 @@ export class PeerServer extends EventEmitter { this._received.bind(this, this.room.players[playerName]) ); + player.conn.on("error", err => { + throw err; + }); + // Send the player info about the room this.send(player, { kind: "room-info", @@ -155,7 +175,7 @@ export class PeerServer extends EventEmitter { ...this.room.info, password: "" }, - players: Object.keys(this.room.players) + players }); // Notify other players @@ -172,6 +192,9 @@ export class PeerServer extends EventEmitter { // Close connection with player player.conn.close(); + // Remove player from player list + delete this.room.players[player.name]; + // Notify other players this.broadcast({ kind: "player-left", -- 2.40.1 From 69f9cb6843605f271f94a2d76626158ebc052f72 Mon Sep 17 00:00:00 2001 From: Hamcha Date: Mon, 7 Oct 2019 12:23:17 +0200 Subject: [PATCH 13/18] Sync player list and player ID with vuex --- src/network/Client.ts | 1 + src/store/network/actions.ts | 16 +++++++++++++++- src/store/network/getters.ts | 12 +++++------- src/store/network/index.ts | 1 + src/store/network/mutations.ts | 8 ++++++++ src/store/network/types.ts | 4 ++-- src/views/Lobby.vue | 27 +++++++++++++++++++++++++-- 7 files changed, 57 insertions(+), 12 deletions(-) diff --git a/src/network/Client.ts b/src/network/Client.ts index ad74990..4ac0305 100644 --- a/src/network/Client.ts +++ b/src/network/Client.ts @@ -23,6 +23,7 @@ export abstract class Client extends EventEmitter { case "room-info": this.roomInfo = data.room; this.players = data.players; + this.emit("handshake"); break; // Someone changed name (or was forced to) case "rename": diff --git a/src/store/network/actions.ts b/src/store/network/actions.ts index b48570d..4afbaed 100644 --- a/src/store/network/actions.ts +++ b/src/store/network/actions.ts @@ -1,4 +1,4 @@ -import { ActionTree } from "vuex"; +import { ActionTree, Commit } from "vuex"; import { AppState } from "../types"; import { NetworkState, StartServerOptions, ConnectOptions } from "./types"; import { @@ -10,10 +10,23 @@ import { ChatMessage } from "@/network"; +function bindClientEvents(commit: Commit, client: Client) { + client.on("handshake", () => { + commit("playerListChanged", client.players); + }); + client.on("player-joined", () => commit("playerListChanged", client.players)); + client.on("player-left", () => commit("playerListChanged", client.players)); + client.on("rename", () => commit("playerListChanged", client.players)); +} + const actions: ActionTree = { startServer({ commit }, options: StartServerOptions) { const local = new LocalClient(options.playerInfo); const server = new PeerServer(options.roomInfo, local, options._customPeer); + server.once("open", id => { + commit("serverAssignedID", id); + }); + bindClientEvents(commit, local); commit("becomeServer", { local, server }); }, @@ -29,6 +42,7 @@ const actions: ActionTree = { client.on("error", err => { commit("connectionError", err); }); + bindClientEvents(commit, client); client.connect(options.serverID); }, diff --git a/src/store/network/getters.ts b/src/store/network/getters.ts index d69b772..d73da9a 100644 --- a/src/store/network/getters.ts +++ b/src/store/network/getters.ts @@ -15,13 +15,7 @@ const getters: GetterTree = { }, sessionID(state): string | null { - switch (state.peerType) { - case "server": - return state.server.id; - case "client": - return state.serverID; - } - return null; + return state.serverID; }, client(state): Client | null { @@ -46,6 +40,10 @@ const getters: GetterTree = { return true; } return false; + }, + + players(state): string[] { + return state.players; } }; diff --git a/src/store/network/index.ts b/src/store/network/index.ts index 9c43e77..3f6e3ef 100644 --- a/src/store/network/index.ts +++ b/src/store/network/index.ts @@ -15,6 +15,7 @@ export const state: NetworkState = { server: null, local: null, serverID: null, + players: [], chatLog: [] }; diff --git a/src/store/network/mutations.ts b/src/store/network/mutations.ts index dae7c3d..56103e3 100644 --- a/src/store/network/mutations.ts +++ b/src/store/network/mutations.ts @@ -32,6 +32,14 @@ const mutations: MutationTree = { receivedChatMessage(state, message: ChatMessage) { state.chatLog.push(message); + }, + + serverAssignedID(state, id: string) { + state.serverID = id; + }, + + playerListChanged(state, players: string[]) { + state.players = players; } }; diff --git a/src/store/network/types.ts b/src/store/network/types.ts index 5d868eb..532518b 100644 --- a/src/store/network/types.ts +++ b/src/store/network/types.ts @@ -16,6 +16,8 @@ export type ConnectionStatus = export interface SharedNetworkState { chatLog: ChatMessage[]; + serverID: string | null; + players: string[]; } export interface NoNetworkState extends SharedNetworkState { @@ -25,7 +27,6 @@ export interface NoNetworkState extends SharedNetworkState { peer: null; server: null; local: null; - serverID: null; } export interface ClientNetworkState extends SharedNetworkState { @@ -33,7 +34,6 @@ export interface ClientNetworkState extends SharedNetworkState { connectionStatus: ConnectionStatus; connectionError?: Error; peer: PeerClient; - serverID: string; } export interface ServerNetworkState extends SharedNetworkState { diff --git a/src/views/Lobby.vue b/src/views/Lobby.vue index 03abd04..6c61f5d 100644 --- a/src/views/Lobby.vue +++ b/src/views/Lobby.vue @@ -80,7 +80,15 @@
- Session ID: {{ sessionID }} + Session ID: {{ sessionID }} +
+
+ Players: +
    +
  • + {{ player }} +
  • +
@@ -160,10 +168,21 @@ } } +.room { + padding: 10px 20px; + margin: 10px; + border: 1px solid rgba($white, 20%); + border-radius: 10px; +} + .only-mobile { display: none; } +.selectable { + user-select: all; +} + @media (max-width: 500px) { .only-full { display: none; @@ -189,6 +208,7 @@ import { Component, Vue } from "vue-property-decorator"; import TopNav from "@/components/Navigation/TopNav.vue"; import { StartServerOptions, ConnectOptions } from "@/store/network/types"; import { Action, Getter } from "vuex-class"; +import { Client } from "@/network"; @Component({ components: { @@ -211,9 +231,12 @@ export default class Lobby extends Vue { @Getter("inRoom", { namespace: "network" }) private inRoom!: boolean; - @Getter("sessionID", { namespace: "network"} ) + @Getter("sessionID", { namespace: "network" }) private sessionID!: string | null; + @Getter("players", { namespace: "network" }) + private players!: string[]; + private data() { return { playerName: -- 2.40.1 From 8e519d0a3426c1c4999f6b8c1112f9e122843e38 Mon Sep 17 00:00:00 2001 From: Hamcha Date: Wed, 9 Oct 2019 17:47:03 +0200 Subject: [PATCH 14/18] Add server player to initial player list --- src/store/network/mutations.ts | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/store/network/mutations.ts b/src/store/network/mutations.ts index 56103e3..6feaaa5 100644 --- a/src/store/network/mutations.ts +++ b/src/store/network/mutations.ts @@ -1,15 +1,12 @@ +import { ChatMessage, LocalClient, PeerClient, PeerServer } from "@/network"; import { MutationTree } from "vuex"; -import { - NetworkState, - ServerNetworkState, - ClientNetworkState, - ConnectionStatus -} from "./types"; -import { LocalClient, PeerServer, PeerClient, ChatMessage } from "@/network"; + +import { ClientNetworkState, ConnectionStatus, NetworkState, ServerNetworkState } from "./types"; const mutations: MutationTree = { becomeServer(state, payload: { local: LocalClient; server: PeerServer }) { state.peerType = "server"; + state.players = [payload.local.name]; (state as ServerNetworkState).local = payload.local; (state as ServerNetworkState).server = payload.server; }, -- 2.40.1 From ef3e10a6c443566a50d3082222cd1be0b53b2cee Mon Sep 17 00:00:00 2001 From: Hamcha Date: Wed, 9 Oct 2019 17:47:13 +0200 Subject: [PATCH 15/18] Add invite URL --- src/router.ts | 21 ++++++++++++++------- src/views/Lobby.vue | 25 ++++++++++++++++++++++++- vue.config.js | 2 +- 3 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/router.ts b/src/router.ts index 1a2b459..05efe11 100644 --- a/src/router.ts +++ b/src/router.ts @@ -1,12 +1,11 @@ +import DeckBuilder from "@/views/DeckBuilder.vue"; +import DraftView from "@/views/Draft.vue"; +import GameView from "@/views/Game.vue"; +import Home from "@/views/Home.vue"; +import Lobby from "@/views/Lobby.vue"; +import SettingsView from "@/views/Settings.vue"; import Vue from "vue"; import Router from "vue-router"; -import Home from "@/views/Home.vue"; -import DeckBuilder from "@/views/DeckBuilder.vue"; -import GameView from "@/views/Game.vue"; -import DraftView from "@/views/Draft.vue"; -import Lobby from "@/views/Lobby.vue"; -import RoomView from "@/views/Room.vue"; -import SettingsView from "@/views/Settings.vue"; Vue.use(Router); @@ -45,6 +44,14 @@ export default new Router({ topnav: "Lobby" } }, + { + path: "/join/:id", + name: "lobby-join", + component: Lobby, + meta: { + topnav: "Lobby" + } + }, { path: "/settings", name: "settings", diff --git a/src/views/Lobby.vue b/src/views/Lobby.vue index 6c61f5d..dbabd6c 100644 --- a/src/views/Lobby.vue +++ b/src/views/Lobby.vue @@ -80,7 +80,10 @@
- Session ID: {{ sessionID }} + Invite your friends: + {{ inviteLink }}
Players: @@ -251,6 +254,13 @@ export default class Lobby extends Vue { }; } + private mounted() { + if ("id" in this.$route.params) { + this.joinSessionID = this.$route.params.id; + this.join(); + } + } + private async create() { this.busy = true; this.startServer({ @@ -277,5 +287,18 @@ export default class Lobby extends Vue { private get canJoin(): boolean { return this.joinSessionID != ""; } + + private get inviteLink(): string { + let subpath = ""; + const joinIndex = location.pathname.indexOf("/join"); + if (joinIndex > 0) { + subpath = location.pathname.substring(0, joinIndex); + } + const lobbyIndex = location.pathname.indexOf("/lobby"); + if (lobbyIndex > 0) { + subpath = location.pathname.substring(0, lobbyIndex); + } + return `${location.origin}${subpath}/join/${this.sessionID}`; + } } diff --git a/vue.config.js b/vue.config.js index e47d307..a07f4bb 100644 --- a/vue.config.js +++ b/vue.config.js @@ -6,7 +6,7 @@ module.exports = { } }, - publicPath: process.env.SUBPATH ? process.env.SUBPATH : "", + publicPath: process.env.SUBPATH ? process.env.SUBPATH : "/", pluginOptions: { gitDescribe: { -- 2.40.1 From 3a5989ea0d1c5861befd65d8a13ea2319f8176f7 Mon Sep 17 00:00:00 2001 From: Hamcha Date: Fri, 11 Oct 2019 16:21:46 +0200 Subject: [PATCH 16/18] Add rename --- src/network/Client.ts | 31 ++++++++--------- src/network/PeerServer.ts | 45 ++++++++++++++++--------- src/store/network/actions.ts | 12 ++----- src/store/network/getters.ts | 12 ++++++- src/store/network/mutations.ts | 3 +- src/store/network/types.ts | 9 +---- src/views/Lobby.vue | 61 +++++++++++++++++++++++++--------- 7 files changed, 106 insertions(+), 67 deletions(-) diff --git a/src/network/Client.ts b/src/network/Client.ts index 4ac0305..6f290e8 100644 --- a/src/network/Client.ts +++ b/src/network/Client.ts @@ -1,10 +1,7 @@ -import { - PeerMetadata, - NetworkMessage, - PasswordResponse, - RoomInfo -} from "./types"; import EventEmitter from "eventemitter3"; +import Vue from "vue"; + +import { NetworkMessage, PasswordResponse, PeerMetadata, RoomInfo } from "./types"; export abstract class Client extends EventEmitter { public metadata: PeerMetadata; @@ -30,18 +27,18 @@ export abstract class Client extends EventEmitter { if (data.oldname == this.metadata.name) { // We got a name change! this.metadata.name = data.newname; - } else { - let idx = this.players.indexOf(data.oldname); - if (idx < 0) { - // Weird - console.warn( - `Someone (${data.oldname}) changed name but wasn't on the player list` - ); - this.players.push(data.newname); - break; - } - this.players[idx] = data.newname; } + + let idx = this.players.indexOf(data.oldname); + if (idx < 0) { + // Weird + console.warn( + `Someone (${data.oldname}) changed name but wasn't on the player list` + ); + this.players.push(data.newname); + break; + } + Vue.set(this.players, idx, data.newname); this.emit("rename", data.oldname, data.newname); break; // A new player joined the room (this includes us) diff --git a/src/network/PeerServer.ts b/src/network/PeerServer.ts index 67e3226..099d614 100644 --- a/src/network/PeerServer.ts +++ b/src/network/PeerServer.ts @@ -1,23 +1,24 @@ +import EventEmitter from "eventemitter3"; import Peer, { DataConnection } from "peerjs"; + +import { LocalClient } from "."; import { - RoomInfo, - PasswordRequest, - Room, + AckMessage, + ChatMessage, ErrorMessage, + JoinMessage, + LeaveMessage, + NetworkMessage, + NetworkPlayer, + PasswordRequest, PasswordResponse, PeerMetadata, - JoinMessage, - RoomInfoMessage, Player, - NetworkMessage, RenameMessage, - LeaveMessage, - NetworkPlayer, - AckMessage, - ChatMessage + Room, + RoomInfo, + RoomInfoMessage, } from "./types"; -import { LocalClient } from "."; -import EventEmitter from "eventemitter3"; // Increment name, add number at the end if not present // Examples: @@ -212,16 +213,30 @@ export class PeerServer extends EventEmitter { this.broadcast(data); } else { // Player is telling someone specifically - if (data.to in this.players) { - this.send(this.players[data.to], data); - } else { + if (!(data.to in this.players)) { this.send(player, { kind: "error", error: `player not found: ${data.to}` }); + return; } + this.send(this.players[data.to], data); } break; + // Player wants to change name + case "rename": + // Make sure new name is valid + data.oldname = player.name; + if (data.newname in this.players) { + this.send(player, { + kind: "error", + error: "name not available" + }); + return; + } + player.name = data.newname; + this.broadcast(data); + break; // Player is leaving! case "leave-req": // If we're leaving, end the server diff --git a/src/store/network/actions.ts b/src/store/network/actions.ts index 4afbaed..ac38a3d 100644 --- a/src/store/network/actions.ts +++ b/src/store/network/actions.ts @@ -1,14 +1,8 @@ +import { ChatMessage, Client, LocalClient, NetworkMessage, PeerClient, PeerServer } from "@/network"; import { ActionTree, Commit } from "vuex"; + import { AppState } from "../types"; -import { NetworkState, StartServerOptions, ConnectOptions } from "./types"; -import { - PeerServer, - LocalClient, - PeerClient, - NetworkMessage, - Client, - ChatMessage -} from "@/network"; +import { ConnectOptions, NetworkState, StartServerOptions } from "./types"; function bindClientEvents(commit: Commit, client: Client) { client.on("handshake", () => { diff --git a/src/store/network/getters.ts b/src/store/network/getters.ts index d73da9a..76f509c 100644 --- a/src/store/network/getters.ts +++ b/src/store/network/getters.ts @@ -1,7 +1,8 @@ +import { Client } from "@/network"; import { GetterTree } from "vuex"; + import { AppState } from "../types"; import { NetworkState } from "./types"; -import { Client } from "@/network"; const getters: GetterTree = { peerID(state): string | null { @@ -32,6 +33,15 @@ const getters: GetterTree = { return state.peerType; }, + busy(state): boolean { + if (state.peerType == "client") { + if (state.connectionStatus == "connecting") { + return true; + } + } + return false; + }, + inRoom(state): boolean { if (state.peerType == "client") { return state.connectionStatus == "connected"; diff --git a/src/store/network/mutations.ts b/src/store/network/mutations.ts index 6feaaa5..6832f2c 100644 --- a/src/store/network/mutations.ts +++ b/src/store/network/mutations.ts @@ -1,4 +1,5 @@ import { ChatMessage, LocalClient, PeerClient, PeerServer } from "@/network"; +import Vue from "vue"; import { MutationTree } from "vuex"; import { ClientNetworkState, ConnectionStatus, NetworkState, ServerNetworkState } from "./types"; @@ -36,7 +37,7 @@ const mutations: MutationTree = { }, playerListChanged(state, players: string[]) { - state.players = players; + Vue.set(state, "players", players); } }; diff --git a/src/store/network/types.ts b/src/store/network/types.ts index 532518b..e810edf 100644 --- a/src/store/network/types.ts +++ b/src/store/network/types.ts @@ -1,11 +1,4 @@ -import { - PeerClient, - PeerServer, - LocalClient, - RoomInfo, - PeerMetadata, - ChatMessage -} from "@/network"; +import { ChatMessage, LocalClient, PeerClient, PeerMetadata, PeerServer, RoomInfo } from "@/network"; import Peer from "peerjs"; export type ConnectionStatus = diff --git a/src/views/Lobby.vue b/src/views/Lobby.vue index dbabd6c..5adeb62 100644 --- a/src/views/Lobby.vue +++ b/src/views/Lobby.vue @@ -93,6 +93,14 @@
+
+ + + + Change name +
@@ -211,7 +219,9 @@ import { Component, Vue } from "vue-property-decorator"; import TopNav from "@/components/Navigation/TopNav.vue"; import { StartServerOptions, ConnectOptions } from "@/store/network/types"; import { Action, Getter } from "vuex-class"; -import { Client } from "@/network"; +import { Client, NetworkMessage } from "@/network"; + +const networkNS = { namespace: "network" }; @Component({ components: { @@ -223,34 +233,41 @@ export default class Lobby extends Vue { private hostMaxPlayers!: number; private hostPassword!: string; private joinSessionID!: string; - private busy!: boolean; + private wantedName!: string; - @Action("startServer", { namespace: "network" }) + @Action("startServer", networkNS) private startServer!: (options: StartServerOptions) => void; - @Action("connect", { namespace: "network" }) + @Action("sendMessage", networkNS) + private sendMessage!: (message: NetworkMessage) => void; + + @Action("connect", networkNS) private connect!: (options: ConnectOptions) => void; - @Getter("inRoom", { namespace: "network" }) + @Getter("inRoom", networkNS) private inRoom!: boolean; - @Getter("sessionID", { namespace: "network" }) + @Getter("busy", networkNS) + private busy!: boolean; + + @Getter("sessionID", networkNS) private sessionID!: string | null; - @Getter("players", { namespace: "network" }) + @Getter("players", networkNS) private players!: string[]; private data() { + const playerName = + "Guest-" + + Math.random() + .toString() + .slice(2, 8); return { - playerName: - "Guest-" + - Math.random() - .toString() - .slice(2, 8), - busy: false, + playerName, hostMaxPlayers: 8, hostPassword: "", - joinSessionID: "" + joinSessionID: "", + wantedName: playerName }; } @@ -262,7 +279,7 @@ export default class Lobby extends Vue { } private async create() { - this.busy = true; + this.wantedName = this.playerName; this.startServer({ playerInfo: { name: this.playerName @@ -275,7 +292,7 @@ export default class Lobby extends Vue { } private async join() { - this.busy = true; + this.wantedName = this.playerName; this.connect({ serverID: this.joinSessionID, playerInfo: { @@ -284,6 +301,14 @@ export default class Lobby extends Vue { }); } + private async changeName() { + this.sendMessage({ + kind: "rename", + oldname: this.playerName, + newname: this.wantedName + }); + } + private get canJoin(): boolean { return this.joinSessionID != ""; } @@ -300,5 +325,9 @@ export default class Lobby extends Vue { } return `${location.origin}${subpath}/join/${this.sessionID}`; } + + private get nameAvailable(): boolean { + return this.wantedName != "" && !this.players.includes(this.wantedName); + } } -- 2.40.1 From 07d5e9b2baac5d1a227cef5e6aa35c6106624d1b Mon Sep 17 00:00:00 2001 From: Hamcha Date: Tue, 15 Oct 2019 14:36:29 +0200 Subject: [PATCH 17/18] Fix rename messages assuming having joined --- src/network/Client.ts | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/src/network/Client.ts b/src/network/Client.ts index 6f290e8..f22e05c 100644 --- a/src/network/Client.ts +++ b/src/network/Client.ts @@ -29,16 +29,21 @@ export abstract class Client extends EventEmitter { this.metadata.name = data.newname; } - let idx = this.players.indexOf(data.oldname); - if (idx < 0) { - // Weird - console.warn( - `Someone (${data.oldname}) changed name but wasn't on the player list` - ); - this.players.push(data.newname); - break; + // Only mutate player list if we have one + // This is because rename messages can be received during the initial + // handshake, to signal a forced name change before joining. + if (this.players) { + let idx = this.players.indexOf(data.oldname); + if (idx < 0) { + // Weird + console.warn( + `Someone (${data.oldname}) changed name but wasn't on the player list` + ); + this.players.push(data.newname); + break; + } + Vue.set(this.players, idx, data.newname); } - Vue.set(this.players, idx, data.newname); this.emit("rename", data.oldname, data.newname); break; // A new player joined the room (this includes us) -- 2.40.1 From dd3b9d3d9e4e195be78f7c1444cc3c74876469b1 Mon Sep 17 00:00:00 2001 From: Hamcha Date: Wed, 16 Oct 2019 10:27:07 +0200 Subject: [PATCH 18/18] Make lobby pretty --- src/views/Lobby.vue | 51 ++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 46 insertions(+), 5 deletions(-) diff --git a/src/views/Lobby.vue b/src/views/Lobby.vue index 5adeb62..1750002 100644 --- a/src/views/Lobby.vue +++ b/src/views/Lobby.vue @@ -85,8 +85,9 @@ >{{ inviteLink }} +
- Players: +
Players
  • {{ player }} @@ -94,12 +95,18 @@
- + +

+ Change name +

- Change name + Leave room
@@ -184,6 +191,32 @@ margin: 10px; border: 1px solid rgba($white, 20%); border-radius: 10px; + display: grid; + grid-template: 50px 1fr / 200px 1fr 300px; + + .info { + grid-column: 1 / max; + grid-row: 1; + } + + .players { + header { + font-weight: bold; + margin-bottom: 5px; + } + grid-row: 2 / max; + grid-column: 1; + } + + .chat { + grid-row: 2 / max; + grid-column: 2; + } + + .player-options { + grid-row: 2 / max; + grid-column: 3; + } } .only-mobile { @@ -211,6 +244,14 @@ } } } + + .room { + display: flex; + flex-flow: column; + & > * { + padding: 10px; + } + } } -- 2.40.1