Compare commits

...

2 Commits

Author SHA1 Message Date
Hamcha 69f9cb6843
Sync player list and player ID with vuex
continuous-integration/drone/pr Build is failing Details
continuous-integration/drone/push Build is failing Details
2019-10-07 12:23:17 +02:00
Hamcha 6b34fe3dfa
Wait for connection and properly clean up players 2019-10-07 12:22:40 +02:00
8 changed files with 83 additions and 15 deletions

View File

@ -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":

View File

@ -34,6 +34,12 @@ function nextName(name: string): string {
return name.substr(0, i) + (Number(name.slice(i)) + 1);
}
function connectionOpen(conn: DataConnection): Promise<void> {
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<RoomInfoMessage>(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<LeaveMessage>({
kind: "player-left",

View File

@ -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<NetworkState, AppState> = {
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<NetworkState, AppState> = {
client.on("error", err => {
commit("connectionError", err);
});
bindClientEvents(commit, client);
client.connect(options.serverID);
},

View File

@ -15,13 +15,7 @@ const getters: GetterTree<NetworkState, AppState> = {
},
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<NetworkState, AppState> = {
return true;
}
return false;
},
players(state): string[] {
return state.players;
}
};

View File

@ -15,6 +15,7 @@ export const state: NetworkState = {
server: null,
local: null,
serverID: null,
players: [],
chatLog: []
};

View File

@ -32,6 +32,14 @@ const mutations: MutationTree<NetworkState> = {
receivedChatMessage(state, message: ChatMessage) {
state.chatLog.push(message);
},
serverAssignedID(state, id: string) {
state.serverID = id;
},
playerListChanged(state, players: string[]) {
state.players = players;
}
};

View File

@ -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 {

View File

@ -80,7 +80,15 @@
</section>
<section class="room" v-else>
<section class="info">
Session ID: {{ sessionID }}
Session ID: <span class="selectable">{{ sessionID }}</span>
</section>
<section class="players">
Players:
<ul>
<li class="selectable" v-for="player in players" :key="player">
{{ player }}
</li>
</ul>
</section>
</section>
</section>
@ -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: