mirror of https://github.com/bra1n/townsquare.git
add player role distribution through live session for ST
This commit is contained in:
parent
53d608288d
commit
f7eef3f8ea
|
@ -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);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -309,7 +309,9 @@ export default {
|
|||
.life,
|
||||
.token,
|
||||
.shroud,
|
||||
.night-order {
|
||||
.night-order,
|
||||
.seat {
|
||||
animation-delay: ($i - 1) * 50ms;
|
||||
transition-delay: ($i - 1) * 50ms;
|
||||
}
|
||||
|
||||
|
|
|
@ -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 } = {}
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue