add player role distribution through live session for ST

This commit is contained in:
Steffen 2020-12-15 22:31:50 +01:00
parent 53d608288d
commit f7eef3f8ea
6 changed files with 124 additions and 41 deletions

View File

@ -30,25 +30,24 @@ const channels = {};
// a new client connects
wss.on("connection", function connection(ws, req) {
ws.channel = req.url
.split("/")
.pop()
.toLocaleLowerCase();
if (ws.channel.match(/-host$/i)) {
ws.isHost = true;
ws.channel = ws.channel.substr(0, ws.channel.length - 5);
// check for another host on this channel
if (
channels[ws.channel] &&
channels[ws.channel].some(
client =>
client !== ws && client.readyState === WebSocket.OPEN && client.isHost
)
) {
console.log(ws.channel, "duplicate host");
ws.close(1000, `The channel "${ws.channel}" already has a host`);
return;
}
// url pattern: clocktower.online/<channel>/<playerId|host>
const url = req.url.toLocaleLowerCase().split("/");
ws.playerId = url.pop();
ws.channel = url.pop();
// check for another host on this channel
if (
ws.playerId === "host" &&
channels[ws.channel] &&
channels[ws.channel].some(
client =>
client !== ws &&
client.readyState === WebSocket.OPEN &&
client.playerId === "host"
)
) {
console.log(ws.channel, "duplicate host");
ws.close(1000, `The channel "${ws.channel}" already has a host`);
return;
}
ws.isAlive = true;
ws.pingStart = new Date().getTime();
@ -81,20 +80,44 @@ wss.on("connection", function connection(ws, req) {
);
return;
}
const isPing = data.match(/^\["ping/i);
if (!isPing) {
console.log(new Date(), wss.clients.size, ws.channel, data);
const messageType = data
.toLocaleLowerCase()
.substr(1)
.split(",", 1)
.pop();
// don't log ping messages
if (messageType !== '"ping"') {
console.log(new Date(), wss.clients.size, ws.channel, ws.playerId, data);
}
channels[ws.channel].forEach(function each(client) {
if (client !== ws && client.readyState === WebSocket.OPEN) {
// inject latency between both clients if ping message
if (isPing && client.latency && ws.latency) {
client.send(data.replace(/latency/, client.latency + ws.latency));
} else {
client.send(data);
}
// handle "direct" messages differently
if (messageType === '"direct"') {
try {
const dataToPlayer = JSON.parse(data)[1];
channels[ws.channel].forEach(function each(client) {
if (
client !== ws &&
client.readyState === WebSocket.OPEN &&
dataToPlayer[client.playerId]
) {
client.send(JSON.stringify(dataToPlayer[client.playerId]));
}
});
} catch (e) {
console.log("error parsing direct message JSON", e);
}
});
} else {
// all other messages
channels[ws.channel].forEach(function each(client) {
if (client !== ws && client.readyState === WebSocket.OPEN) {
// inject latency between both clients if ping message
if (messageType === '"ping"' && client.latency && ws.latency) {
client.send(data.replace(/latency/, client.latency + ws.latency));
} else {
client.send(data);
}
}
});
}
});
});

View File

@ -108,6 +108,10 @@
Copy player link
<em><font-awesome-icon icon="copy"/></em>
</li>
<li v-if="!session.isSpectator" @click="distributeRoles">
Distribute Characters
<em><font-awesome-icon icon="theater-masks"/></em>
</li>
<li
v-if="session.voteHistory.length"
@click="toggleModal('voteHistory')"
@ -258,6 +262,17 @@ export default {
}
});
},
distributeRoles() {
if (this.session.isSpectator) return;
const popup =
"Do you want to distribute assigned characters to all SEATED players?";
if (confirm(popup)) {
this.$store.commit("session/distributeRoles", true);
setTimeout((() => {
this.$store.commit("session/distributeRoles", false);
}).bind(this), 2000);
}
},
joinSession() {
const sessionId = prompt(
"Enter the channel number / name of the session you want to join"

View File

@ -86,6 +86,7 @@
icon="chair"
v-if="player.id && session.sessionId"
class="seat"
:class="{ highlight: session.isRolesDistributed }"
/>
<!-- Ghost vote icon -->
@ -136,7 +137,7 @@
v-if="player.id && session.sessionId"
>
<font-awesome-icon icon="chair" />
Vacate seat
Empty seat
</li>
</template>
<li
@ -613,6 +614,20 @@ li.move:not(.from) .player .overlay svg.move {
filter: drop-shadow(0 0 3px black);
cursor: default;
z-index: 2;
&.highlight {
animation-iteration-count: 1;
animation: redToWhite 1s normal forwards;
}
}
// highlight animation
@keyframes redToWhite {
from {
color: $demon;
}
to {
color: white;
}
}
.player.you .seat {

View File

@ -309,7 +309,9 @@ export default {
.life,
.token,
.shroud,
.night-order {
.night-order,
.seat {
animation-delay: ($i - 1) * 50ms;
transition-delay: ($i - 1) * 50ms;
}

View File

@ -29,7 +29,8 @@ const state = () => ({
lockedVote: 0,
votingSpeed: 3000,
isVoteInProgress: false,
voteHistory: []
voteHistory: [],
isRolesDistributed: false
});
const getters = {};
@ -46,6 +47,7 @@ const mutations = {
setVotingSpeed: set("votingSpeed"),
setVoteInProgress: set("isVoteInProgress"),
claimSeat: set("claimedSeat"),
distributeRoles: set("isRolesDistributed"),
nomination(
state,
{ nomination, votes, votingSpeed, lockedVote, isVoteInProgress } = {}

View File

@ -1,10 +1,8 @@
import rolesJSON from "../roles.json";
class LiveSession {
constructor(store) {
//this._wss = "ws://localhost:8081/";
this._wss = "wss://live.clocktower.online:8080/";
this._wss = "wss://baumgart.biz:8080/"; //todo: delete this
//this._wss = "ws://localhost:8081/";
this._socket = null;
this._isSpectator = true;
this._gamestate = [];
@ -28,7 +26,10 @@ class LiveSession {
_open(channel) {
this.disconnect();
this._socket = new WebSocket(
this._wss + channel + (this._isSpectator ? "" : "-host")
this._wss +
channel +
"/" +
(this._isSpectator ? this._store.state.session.playerId : "host")
);
this._socket.addEventListener("message", this._handleMessage.bind(this));
this._socket.onopen = this._onOpen.bind(this);
@ -283,7 +284,7 @@ class LiveSession {
});
// roles are special, because of travelers
if (roleId && player.role.id !== roleId) {
const role = rolesJSON.find(r => r.id === roleId);
const role = this._store.state.roles.get(roleId);
this._store.commit("players/update", {
player,
property: "role",
@ -430,8 +431,8 @@ class LiveSession {
value: {}
});
} else {
// load traveler role
const role = rolesJSON.find(r => r.id === value);
// load role
const role = this._store.state.roles.get(value);
this._store.commit("players/update", {
player,
property: "role",
@ -550,6 +551,26 @@ class LiveSession {
this._handlePing([true, value]);
}
/**
* Distribute player roles to all seated players in a direct message.
* This will be split server side so that each player only receives their own (sub)message.
*/
distributeRoles() {
if (this._isSpectator) return;
const message = {};
this._store.state.players.players.forEach((player, index) => {
if (player.id && player.role) {
message[player.id] = [
"player",
{ index, property: "role", value: player.role.id }
];
}
if (Object.keys(message).length) {
this._send("direct", message);
}
});
}
/**
* A player nomination. ST only
* This also syncs the voting speed to the players.
@ -695,6 +716,11 @@ export default store => {
case "session/claimSeat":
session.claimSeat(payload);
break;
case "session/distributeRoles":
if (payload) {
session.distributeRoles();
}
break;
case "session/nomination":
session.nomination(payload);
break;