Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
dist/
node_modules/
node_modules/
config/*
!config/*.example.*
29 changes: 26 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
* [Getting Started](#getting-started)
* [Prerequisites](#prerequisites)
* [Installation](#installation)
* [Customizing Peer to Peer Behavior](#customizing-peer-to-peer-behavior)
* [Contributing](#contributing)
* [License](#license)

Expand All @@ -54,7 +55,7 @@ To get up and running quickly, you can deploy to Heroku using the button below

[![Deploy](https://www.herokucdn.com/deploy/button.svg)](https://heroku.com/deploy)

This will deploy an instance of the crewlink-server. You can get the URL of your server by using the app name that you gave when you launched the app on heroku and appending `.herokuapp.com`. You can also find the URL of your server by going to "Settings", scrolling down to "Domains", and removing the `https://` and trailing slash from the url. Using this URL, follow step 4 of the [installation instructions](https://github.com/ottomated/CrewLink-server#manual-installation) to connect your client to your server instance.
This will deploy an instance of the crewlink-server. You can get the URL of your server by using the app name that you gave when you launched the app on heroku and appending `.herokuapp.com`. You can also find the URL of your server by going to "Settings", scrolling down to "Domains". Using this URL, follow step 4 of the [installation instructions](https://github.com/ottomated/CrewLink-server#manual-installation) to connect your client to your server instance.

## Docker Quickstart

Expand Down Expand Up @@ -111,8 +112,30 @@ yarn install
```JS
yarn start
```
4. Copy your server's IP and port into CrewLink settings. Make sure everyone in your lobby is using the same server.

4. Copy your server URL into CrewLink settings. Make sure everyone in your lobby is using the same server.
### Customizing Peer to Peer Behavior
CrewLink-server should work out of the box for most people. You may, however, come across people who are unable to hear
other people, no matter what they try. This could be because of a NAT or firewall that is preventing peer to peer
connections. In this case you may want to set up your own relay server to act as a middleman for your voice traffic.
CrewLink needs a specific type of server for this called a TURN server.

You can configure a TURN server by creating a file called ``peerConfig.yml`` in the config folder. Use the
provided example as a template. A good, open source TURN server implementation is
[Coturn](https://github.com/coturn/coturn). A relay server may also be desirable in case you want to prevent CrewLink
players from detecting the IP addresses of everyone in the same Among Us room using your voice server.

CAUTION: Hosting your own TURN server is a great way to solve most connection issues but they're not without problems.
These servers, by design, let anyone with the credentials relay packets to anywhere. Because you are sending the TURN
credentials to everyone connecting to your CrewLink server, obtaining them becomes very easy. If you choose to set one
up, make sure you:
- Know what you're doing
- Configure the server in a way that disallows relaying packets to places that make no sense (localhost, private networks, etc)
- Consider limiting the bandwidth per user and across the whole TURN server
- Keep an eye on TURN server activity while you're running one
- Change TURN credentials regularly
- This list is not exhaustive, see point 1

It is because of this that you should only deploy a TURN server for small, private CrewLink servers.
<!-- CONTRIBUTING -->
## Contributing

Expand Down
35 changes: 35 additions & 0 deletions config/peerConfig.example.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# IN ORDER TO USE THIS CONFIG FILE, RENAME IT AND REMOVE THE ".example" PART OF THE FILENAME!
#
# CrewLink peer to peer connection configuration.
#
# This file lets you modify how CrewLink connects players to each other.
#
# CrewLink uses peer to peer connections to send voice and game state data between players. This means that every player
# has to be able to connect to every other player some way or another in order for CrewLink to work. Below you can tweak
# the mechanisms CrewLink uses to establish peer to peer connections.

# Force CrewLink to only work through TURN servers. This is great if you want to protect the IP addresses of players
# as no direct connection will be made. At least one TURN server is required if set to true.
forceRelayOnly: false

# If you run into connection issues, you may want to add your own TURN server here.
#
# CAUTION: Hosting your own TURN server is a great way to solve most connection issues but they're not without problems.
# These servers, by design, let anyone with the credentials relay packets to anywhere. Because you are sending the TURN
# credentials to everyone connecting to your CrewLink server, obtaining them becomes very easy. If you choose to set one
# up, make sure you:
# - Know what you're doing
# - Configure the server in a way that disallows relaying packets to places that make no sense (localhost, private networks, etc)
# - Consider limiting the bandwidth per user and across the whole TURN server
# - Keep an eye on TURN server activity while you're running one
# - Change TURN credentials regularly
# - This list is not exhaustive, see point 1
iceServers:
# Google's STUN server is used by default
- urls: 'stun:stun.l.google.com:19302'
# - urls: 'turn:example.com'
# username: 'TurnUsername'
# credential: 'TurnPassword'
# - urls: 'stun:example.com'
# username: 'StunUsername'
# credential: 'StunPassword'
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
"morgan": "^1.10.0",
"pug": "^3.0.0",
"socket.io": "^2.3.0",
"tracer": "^1.1.4"
"tracer": "^1.1.4",
"yaml": "^1.10.0"
},
"scripts": {
"start": "yarn compile && node dist/index.js",
Expand Down
5 changes: 5 additions & 0 deletions src/ICEServer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export interface ICEServer {
urls: string|string[];
username?: string;
credential?: string;
}
39 changes: 26 additions & 13 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import express from 'express';
import { Server } from 'http';
import { Server as HttpsServer } from 'https';
import { readFileSync, readdirSync } from 'fs';
import { readFileSync } from 'fs';
import { join } from 'path';
import socketIO from 'socket.io';
import Tracer from 'tracer';
import morgan from 'morgan';
import peerConfig from './peerConfig';
import { ICEServer } from './ICEServer';

const supportedCrewLinkVersions = new Set(['1.2.0', '1.2.1']);
const httpsEnabled = !!process.env.HTTPS;
Expand All @@ -28,6 +30,13 @@ if (httpsEnabled) {
} else {
server = new Server(app);
}

let address = process.env.ADDRESS;
if (!address) {
logger.error('You must set the ADDRESS environment variable.');
process.exit(1);
}

const io = socketIO(server);

const clients = new Map<string, Client>();
Expand All @@ -42,15 +51,15 @@ interface Signal {
to: string;
}

app.set('view engine', 'pug')
app.use(morgan('combined'))
interface ClientPeerConfig {
forceRelayOnly: boolean;
iceServers: ICEServer[]
}

app.set('view engine', 'pug');
app.use(morgan('combined'));

let connectionCount = 0;
let address = process.env.ADDRESS;
if (!address) {
logger.error('You must set the ADDRESS environment variable.');
process.exit(1);
}

app.get('/', (_, res) => {
res.render('index', { connectionCount, address });
Expand Down Expand Up @@ -88,6 +97,13 @@ io.on('connection', (socket: socketIO.Socket) => {
logger.info("Total connected: %d", connectionCount);
let code: string | null = null;

const clientPeerConfig: ClientPeerConfig = {
forceRelayOnly: peerConfig.forceRelayOnly,
iceServers: peerConfig.iceServers? [...peerConfig.iceServers] : []
}

socket.emit('clientPeerConfig', clientPeerConfig);

socket.on('join', (c: string, id: number, clientId: number) => {
if (typeof c !== 'string' || typeof id !== 'number' || typeof clientId !== 'number') {
socket.disconnect();
Expand Down Expand Up @@ -163,10 +179,7 @@ io.on('connection', (socket: socketIO.Socket) => {
connectionCount--;
logger.info("Total connected: %d", connectionCount);
})

})
});

server.listen(port);
(async () => {
logger.info('CrewLink Server started: %s', address);
})();
logger.info('CrewLink Server started: %s', address);
31 changes: 31 additions & 0 deletions src/peerConfig.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import YAML from 'yaml';
import path from 'path';
import fs from 'fs';
import { ICEServer } from './ICEServer';

const PEER_CONFIG_PATH = path.join(__dirname, '..', 'config', 'peerConfig.yml');

interface PeerConfig {
forceRelayOnly: boolean;
iceServers?: ICEServer[]
}

const DEFAULT_PEER_CONFIG: PeerConfig = {
forceRelayOnly: false,
iceServers: [
{
urls: 'stun:stun.l.google.com:19302'
}
]
};

let peerConfig = DEFAULT_PEER_CONFIG;
if (fs.existsSync(PEER_CONFIG_PATH)) {
try {
peerConfig = YAML.parse(fs.readFileSync(PEER_CONFIG_PATH).toString('utf8'));
} catch (err) {
console.error(`Unable to load peer config file. Make sure it is valid YAML.\n${err}`);
}
}

export default peerConfig
89 changes: 51 additions & 38 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,22 @@
# yarn lockfile v1


"@babel/helper-validator-identifier@^7.10.4":
version "7.10.4"
resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz#a78c7a7251e01f616512d31b10adcf52ada5e0d2"
integrity sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==
"@babel/helper-validator-identifier@^7.12.11":
version "7.12.11"
resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz#c9a1f021917dcb5ccf0d4e453e399022981fc9ed"
integrity sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==

"@babel/parser@^7.6.0", "@babel/parser@^7.9.6":
version "7.12.7"
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.12.7.tgz#fee7b39fe809d0e73e5b25eecaf5780ef3d73056"
integrity sha512-oWR02Ubp4xTLCAqPRiNIuMVgNO5Aif/xpXtabhzW2HWUD47XJsAB4Zd/Rg30+XeQA3juXigV7hlquOTmwqLiwg==
version "7.12.11"
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.12.11.tgz#9ce3595bcd74bc5c466905e86c535b8b25011e79"
integrity sha512-N3UxG+uuF4CMYoNj8AhnbAcJF0PiuJ9KHuy1lQmkYsxTer/MAH9UBNHsBoAX/4s6NvlDD047No8mYVGGzLL4hg==

"@babel/types@^7.6.1", "@babel/types@^7.9.6":
version "7.12.7"
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.12.7.tgz#6039ff1e242640a29452c9ae572162ec9a8f5d13"
integrity sha512-MNyI92qZq6jrQkXvtIiykvl4WtoRrVV9MPn+ZfsoEENjiWcBQ3ZSHrkxnJWgWtLX3XXqX5hrSQ+X69wkmesXuQ==
version "7.12.12"
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.12.12.tgz#4608a6ec313abbd87afa55004d373ad04a96c299"
integrity sha512-lnIX7piTxOH22xE7fDXDbSHg9MM1/6ORnafpJmov5rs0kX5g4BZxeXNJLXsMRiO0U5Rb8/FvMS6xlTnTHvxonQ==
dependencies:
"@babel/helper-validator-identifier" "^7.10.4"
"@babel/helper-validator-identifier" "^7.12.11"
lodash "^4.17.19"
to-fast-properties "^2.0.0"

Expand All @@ -30,9 +30,9 @@
"@types/node" "*"

"@types/connect@*":
version "3.4.33"
resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.33.tgz#31610c901eca573b8713c3330abc6e6b9f588546"
integrity sha512-2+FrkXY4zllzTNfJth7jOqEHC+enpLeGslEhpnTAkg21GkRrWV4SsAtqchtT4YS9/nODBU2/ZfsBY2X4J/dX7A==
version "3.4.34"
resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.34.tgz#170a40223a6d666006d93ca128af2beb1d9b1901"
integrity sha512-ePPA/JuI+X0vb+gSWlPKOY0NdNAie/rPUqX2GUPpbZwiKTkSPhjXWuee47E4MtE54QVzGCQMQkAL6JhV2E1+cQ==
dependencies:
"@types/node" "*"

Expand All @@ -44,18 +44,18 @@
"@types/node" "*"

"@types/express-serve-static-core@*":
version "4.17.13"
resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.13.tgz#d9af025e925fc8b089be37423b8d1eac781be084"
integrity sha512-RgDi5a4nuzam073lRGKTUIaL3eF2+H7LJvJ8eUnCI0wA6SNjXc44DCmWNiTLs/AZ7QlsFWZiw/gTG3nSQGL0fA==
version "4.17.17"
resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.17.tgz#6ba02465165b6c9c3d8db3a28def6b16fc9b70f5"
integrity sha512-YYlVaCni5dnHc+bLZfY908IG1+x5xuibKZMGv8srKkvtul3wUuanYvpIj9GXXoWkQbaAdR+kgX46IETKUALWNQ==
dependencies:
"@types/node" "*"
"@types/qs" "*"
"@types/range-parser" "*"

"@types/express@^4.17.8":
version "4.17.8"
resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.8.tgz#3df4293293317e61c60137d273a2e96cd8d5f27a"
integrity sha512-wLhcKh3PMlyA2cNAB9sjM1BntnhPMiM0JOBwPBqttjHev2428MLEB4AYVN+d8s2iyCVZac+o41Pflm/ZH5vLXQ==
version "4.17.9"
resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.9.tgz#f5f2df6add703ff28428add52bdec8a1091b0a78"
integrity sha512-SDzEIZInC4sivGIFY4Sz1GG6J9UObPwCInYJjko2jzOf/Imx/dlpume6Xxwj1ORL82tBbmN4cPDIDkLbWHk9hw==
dependencies:
"@types/body-parser" "*"
"@types/express-serve-static-core" "*"
Expand All @@ -75,9 +75,9 @@
"@types/node" "*"

"@types/node@*":
version "14.14.3"
resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.3.tgz#e1c09064121f894baaad2bd9f12ce4a41bffb274"
integrity sha512-33/L34xS7HVUx23e0wOT2V1qPF1IrHgQccdJVm9uXGTB9vFBrrzBtkQymT8VskeKOxjz55MSqMv0xuLq+u98WQ==
version "14.14.16"
resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.16.tgz#3cc351f8d48101deadfed4c9e4f116048d437b4b"
integrity sha512-naXYePhweTi+BMv11TgioE2/FXU4fSl29HAH1ffxVciNsH3rYXjNP2yM8wqmSm7jS20gM8TIklKiTen+1iVncw==

"@types/qs@*":
version "6.9.5"
Expand All @@ -90,20 +90,28 @@
integrity sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA==

"@types/serve-static@*":
version "1.13.6"
resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.13.6.tgz#866b1b8dec41c36e28c7be40ac725b88be43c5c1"
integrity sha512-nuRJmv7jW7VmCVTn+IgYDkkbbDGyIINOeu/G0d74X3lm6E5KfMeQPJhxIt1ayQeQB3cSxvYs1RA/wipYoFB4EA==
version "1.13.8"
resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.13.8.tgz#851129d434433c7082148574ffec263d58309c46"
integrity sha512-MoJhSQreaVoL+/hurAZzIm8wafFR6ajiTM1m4A0kv6AGeVBl4r4pOV8bGFrjjq1sGxDTnCoF8i22o0/aE5XCyA==
dependencies:
"@types/mime" "*"
"@types/node" "*"

"@types/socket.io-parser@*":
version "2.2.1"
resolved "https://registry.yarnpkg.com/@types/socket.io-parser/-/socket.io-parser-2.2.1.tgz#dc94aed303839487f4975249a32a548109ea3647"
integrity sha512-+JNb+7N7tSINyXPxAJb62+NcpC1x/fPn7z818W4xeNCdPTp6VsO/X8fCsg6+ug4a56m1v9sEiTIIUKVupcHOFQ==
dependencies:
"@types/node" "*"

"@types/socket.io@^2.1.11":
version "2.1.11"
resolved "https://registry.yarnpkg.com/@types/socket.io/-/socket.io-2.1.11.tgz#e0d6759880e5f9818d5297a3328b36641bae996b"
integrity sha512-bVprmqPhJMLb9ZCm8g0Xy8kwBFRbnanOWSxzWkDkkIwxTvud5tKMfAJymXX6LQbizUKCS1yima7JM4BeLqjNqA==
version "2.1.12"
resolved "https://registry.yarnpkg.com/@types/socket.io/-/socket.io-2.1.12.tgz#91187f826a5dd2ed1113e935815bbaf9627e5cb0"
integrity sha512-oStc5VFkpb0AsjOxQUj9ztX5Iziatyla/rjZTYbFGoVrrKwd+JU2mtxk7iSl5RGYx9WunLo6UXW1fBzQok/ZyA==
dependencies:
"@types/engine.io" "*"
"@types/node" "*"
"@types/socket.io-parser" "*"

accepts@~1.3.4, accepts@~1.3.7:
version "1.3.7"
Expand Down Expand Up @@ -639,9 +647,9 @@ ms@2.1.1:
integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==

ms@^2.1.1:
version "2.1.2"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
version "2.1.3"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==

negotiator@0.6.2:
version "0.6.2"
Expand Down Expand Up @@ -1003,9 +1011,9 @@ type-is@~1.6.17, type-is@~1.6.18:
mime-types "~2.1.24"

typescript@^4.0.5:
version "4.0.5"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.0.5.tgz#ae9dddfd1069f1cb5beb3ef3b2170dd7c1332389"
integrity sha512-ywmr/VrTVCmNTJ6iV2LwIrfG1P+lv6luD8sUJs+2eI9NLGigaN+nUQc13iHqisq7bra9lnmUSYqbJvegraBOPQ==
version "4.1.3"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.1.3.tgz#519d582bd94cba0cf8934c7d8e8467e473f53bb7"
integrity sha512-B3ZIOf1IKeH2ixgHhj6la6xdwR9QrLC5d1VKeCSY4tvkqhF2eqd9O7txNlS0PO3GrBAFIdr3L1ndNwteUbZLYg==

unpipe@1.0.0, unpipe@~1.0.0:
version "1.0.0"
Expand Down Expand Up @@ -1038,9 +1046,9 @@ with@^7.0.0:
babel-walk "3.0.0-canary-5"

ws@^7.1.2:
version "7.3.1"
resolved "https://registry.yarnpkg.com/ws/-/ws-7.3.1.tgz#d0547bf67f7ce4f12a72dfe31262c68d7dc551c8"
integrity sha512-D3RuNkynyHmEJIpD2qrgVkc9DQ23OrN/moAwZX4L8DfvszsJxpjQuUq3LMx6HoYji9fbIOBY18XWBsAux1ZZUA==
version "7.4.1"
resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.1.tgz#a333be02696bd0e54cea0434e21dcc8a9ac294bb"
integrity sha512-pTsP8UAfhy3sk1lSk/O/s4tjD0CRwvMnzvwr4OKGX7ZvqZtUyx4KIJB5JWbkykPoc55tixMGgTNoh3k4FkNGFQ==

ws@~6.1.0:
version "6.1.4"
Expand All @@ -1054,6 +1062,11 @@ xmlhttprequest-ssl@~1.5.4:
resolved "https://registry.yarnpkg.com/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz#c2876b06168aadc40e57d97e81191ac8f4398b3e"
integrity sha1-wodrBhaKrcQOV9l+gRkayPQ5iz4=

yaml@^1.10.0:
version "1.10.0"
resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.0.tgz#3b593add944876077d4d683fee01081bd9fff31e"
integrity sha512-yr2icI4glYaNG+KWONODapy2/jDdMSDnrONSjblABjD9B4Z5LgiircSt8m8sRZFNi08kG9Sm0uSHtEmP3zaEGg==

yeast@0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/yeast/-/yeast-0.1.2.tgz#008e06d8094320c372dbc2f8ed76a0ca6c8ac419"
Expand Down