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,26 +30,25 @@ const channels = {};
|
||||||
|
|
||||||
// a new client connects
|
// a new client connects
|
||||||
wss.on("connection", function connection(ws, req) {
|
wss.on("connection", function connection(ws, req) {
|
||||||
ws.channel = req.url
|
// url pattern: clocktower.online/<channel>/<playerId|host>
|
||||||
.split("/")
|
const url = req.url.toLocaleLowerCase().split("/");
|
||||||
.pop()
|
ws.playerId = url.pop();
|
||||||
.toLocaleLowerCase();
|
ws.channel = url.pop();
|
||||||
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
|
// check for another host on this channel
|
||||||
if (
|
if (
|
||||||
|
ws.playerId === "host" &&
|
||||||
channels[ws.channel] &&
|
channels[ws.channel] &&
|
||||||
channels[ws.channel].some(
|
channels[ws.channel].some(
|
||||||
client =>
|
client =>
|
||||||
client !== ws && client.readyState === WebSocket.OPEN && client.isHost
|
client !== ws &&
|
||||||
|
client.readyState === WebSocket.OPEN &&
|
||||||
|
client.playerId === "host"
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
console.log(ws.channel, "duplicate host");
|
console.log(ws.channel, "duplicate host");
|
||||||
ws.close(1000, `The channel "${ws.channel}" already has a host`);
|
ws.close(1000, `The channel "${ws.channel}" already has a host`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
ws.isAlive = true;
|
ws.isAlive = true;
|
||||||
ws.pingStart = new Date().getTime();
|
ws.pingStart = new Date().getTime();
|
||||||
ws.counter = 0;
|
ws.counter = 0;
|
||||||
|
@ -81,20 +80,44 @@ wss.on("connection", function connection(ws, req) {
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const isPing = data.match(/^\["ping/i);
|
const messageType = data
|
||||||
if (!isPing) {
|
.toLocaleLowerCase()
|
||||||
console.log(new Date(), wss.clients.size, ws.channel, data);
|
.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);
|
||||||
}
|
}
|
||||||
|
// 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) {
|
channels[ws.channel].forEach(function each(client) {
|
||||||
if (client !== ws && client.readyState === WebSocket.OPEN) {
|
if (client !== ws && client.readyState === WebSocket.OPEN) {
|
||||||
// inject latency between both clients if ping message
|
// inject latency between both clients if ping message
|
||||||
if (isPing && client.latency && ws.latency) {
|
if (messageType === '"ping"' && client.latency && ws.latency) {
|
||||||
client.send(data.replace(/latency/, client.latency + ws.latency));
|
client.send(data.replace(/latency/, client.latency + ws.latency));
|
||||||
} else {
|
} else {
|
||||||
client.send(data);
|
client.send(data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -108,6 +108,10 @@
|
||||||
Copy player link
|
Copy player link
|
||||||
<em><font-awesome-icon icon="copy"/></em>
|
<em><font-awesome-icon icon="copy"/></em>
|
||||||
</li>
|
</li>
|
||||||
|
<li v-if="!session.isSpectator" @click="distributeRoles">
|
||||||
|
Distribute Characters
|
||||||
|
<em><font-awesome-icon icon="theater-masks"/></em>
|
||||||
|
</li>
|
||||||
<li
|
<li
|
||||||
v-if="session.voteHistory.length"
|
v-if="session.voteHistory.length"
|
||||||
@click="toggleModal('voteHistory')"
|
@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() {
|
joinSession() {
|
||||||
const sessionId = prompt(
|
const sessionId = prompt(
|
||||||
"Enter the channel number / name of the session you want to join"
|
"Enter the channel number / name of the session you want to join"
|
||||||
|
|
|
@ -86,6 +86,7 @@
|
||||||
icon="chair"
|
icon="chair"
|
||||||
v-if="player.id && session.sessionId"
|
v-if="player.id && session.sessionId"
|
||||||
class="seat"
|
class="seat"
|
||||||
|
:class="{ highlight: session.isRolesDistributed }"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<!-- Ghost vote icon -->
|
<!-- Ghost vote icon -->
|
||||||
|
@ -136,7 +137,7 @@
|
||||||
v-if="player.id && session.sessionId"
|
v-if="player.id && session.sessionId"
|
||||||
>
|
>
|
||||||
<font-awesome-icon icon="chair" />
|
<font-awesome-icon icon="chair" />
|
||||||
Vacate seat
|
Empty seat
|
||||||
</li>
|
</li>
|
||||||
</template>
|
</template>
|
||||||
<li
|
<li
|
||||||
|
@ -613,6 +614,20 @@ li.move:not(.from) .player .overlay svg.move {
|
||||||
filter: drop-shadow(0 0 3px black);
|
filter: drop-shadow(0 0 3px black);
|
||||||
cursor: default;
|
cursor: default;
|
||||||
z-index: 2;
|
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 {
|
.player.you .seat {
|
||||||
|
|
|
@ -309,7 +309,9 @@ export default {
|
||||||
.life,
|
.life,
|
||||||
.token,
|
.token,
|
||||||
.shroud,
|
.shroud,
|
||||||
.night-order {
|
.night-order,
|
||||||
|
.seat {
|
||||||
|
animation-delay: ($i - 1) * 50ms;
|
||||||
transition-delay: ($i - 1) * 50ms;
|
transition-delay: ($i - 1) * 50ms;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,8 @@ const state = () => ({
|
||||||
lockedVote: 0,
|
lockedVote: 0,
|
||||||
votingSpeed: 3000,
|
votingSpeed: 3000,
|
||||||
isVoteInProgress: false,
|
isVoteInProgress: false,
|
||||||
voteHistory: []
|
voteHistory: [],
|
||||||
|
isRolesDistributed: false
|
||||||
});
|
});
|
||||||
|
|
||||||
const getters = {};
|
const getters = {};
|
||||||
|
@ -46,6 +47,7 @@ const mutations = {
|
||||||
setVotingSpeed: set("votingSpeed"),
|
setVotingSpeed: set("votingSpeed"),
|
||||||
setVoteInProgress: set("isVoteInProgress"),
|
setVoteInProgress: set("isVoteInProgress"),
|
||||||
claimSeat: set("claimedSeat"),
|
claimSeat: set("claimedSeat"),
|
||||||
|
distributeRoles: set("isRolesDistributed"),
|
||||||
nomination(
|
nomination(
|
||||||
state,
|
state,
|
||||||
{ nomination, votes, votingSpeed, lockedVote, isVoteInProgress } = {}
|
{ nomination, votes, votingSpeed, lockedVote, isVoteInProgress } = {}
|
||||||
|
|
|
@ -1,10 +1,8 @@
|
||||||
import rolesJSON from "../roles.json";
|
|
||||||
|
|
||||||
class LiveSession {
|
class LiveSession {
|
||||||
constructor(store) {
|
constructor(store) {
|
||||||
//this._wss = "ws://localhost:8081/";
|
|
||||||
this._wss = "wss://live.clocktower.online:8080/";
|
this._wss = "wss://live.clocktower.online:8080/";
|
||||||
this._wss = "wss://baumgart.biz:8080/"; //todo: delete this
|
this._wss = "wss://baumgart.biz:8080/"; //todo: delete this
|
||||||
|
//this._wss = "ws://localhost:8081/";
|
||||||
this._socket = null;
|
this._socket = null;
|
||||||
this._isSpectator = true;
|
this._isSpectator = true;
|
||||||
this._gamestate = [];
|
this._gamestate = [];
|
||||||
|
@ -28,7 +26,10 @@ class LiveSession {
|
||||||
_open(channel) {
|
_open(channel) {
|
||||||
this.disconnect();
|
this.disconnect();
|
||||||
this._socket = new WebSocket(
|
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.addEventListener("message", this._handleMessage.bind(this));
|
||||||
this._socket.onopen = this._onOpen.bind(this);
|
this._socket.onopen = this._onOpen.bind(this);
|
||||||
|
@ -283,7 +284,7 @@ class LiveSession {
|
||||||
});
|
});
|
||||||
// roles are special, because of travelers
|
// roles are special, because of travelers
|
||||||
if (roleId && player.role.id !== roleId) {
|
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", {
|
this._store.commit("players/update", {
|
||||||
player,
|
player,
|
||||||
property: "role",
|
property: "role",
|
||||||
|
@ -430,8 +431,8 @@ class LiveSession {
|
||||||
value: {}
|
value: {}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
// load traveler role
|
// load role
|
||||||
const role = rolesJSON.find(r => r.id === value);
|
const role = this._store.state.roles.get(value);
|
||||||
this._store.commit("players/update", {
|
this._store.commit("players/update", {
|
||||||
player,
|
player,
|
||||||
property: "role",
|
property: "role",
|
||||||
|
@ -550,6 +551,26 @@ class LiveSession {
|
||||||
this._handlePing([true, value]);
|
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
|
* A player nomination. ST only
|
||||||
* This also syncs the voting speed to the players.
|
* This also syncs the voting speed to the players.
|
||||||
|
@ -695,6 +716,11 @@ export default store => {
|
||||||
case "session/claimSeat":
|
case "session/claimSeat":
|
||||||
session.claimSeat(payload);
|
session.claimSeat(payload);
|
||||||
break;
|
break;
|
||||||
|
case "session/distributeRoles":
|
||||||
|
if (payload) {
|
||||||
|
session.distributeRoles();
|
||||||
|
}
|
||||||
|
break;
|
||||||
case "session/nomination":
|
case "session/nomination":
|
||||||
session.nomination(payload);
|
session.nomination(payload);
|
||||||
break;
|
break;
|
||||||
|
|
Loading…
Reference in New Issue