Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow server host to decide peer to peer behavior #28

Open
wants to merge 20 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 7 commits
Commits
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.*
21 changes: 19 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@
* [Getting Started](#getting-started)
* [Prerequisites](#prerequisites)
* [Installation](#installation)
* [Fixing Connection Issues](#fixing-connection-issue)
* [Protecting Player IP Addresses](#protecting-player-ip-addresses)
* [Contributing](#contributing)
* [License](#license)

Expand All @@ -44,7 +46,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 @@ -101,7 +103,22 @@ 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.

### Fixing Connection Issues

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. As of yet CrewLink does not come with one by
default.

You can configure a turn server by creating a file called ``peer-config.yml`` in the config folder. Use the
sjoerd108 marked this conversation as resolved.
Show resolved Hide resolved
Provided example as a template. A good, open source TURN server implementation is [Coturn](https://github.com/coturn/coturn).
sjoerd108 marked this conversation as resolved.
Show resolved Hide resolved

### Protecting Player IP Addresses
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 CrewLink. Check the section above on how to set one up.

<!-- CONTRIBUTING -->
## Contributing
Expand Down
32 changes: 32 additions & 0 deletions config/peer-config.example.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# 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 establish voice calls 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. If set to true, a TURN server is required.
forceRelayOnly: false

# STUN servers
# A part of establishing peer to peer connections is finding out the IP address of each participant and letting people
# connect directly to each other. A stun server, simply put, allows you to find out your public IP address and port,
# which is something other participants need to know in order to connect to you. By default CrewLink will use one of
# Google's STUN servers.
stunServers:
- url: 'stun:stun.l.google.com:19302'

# TURN servers:
# Sometimes you find yourself behind a strict firewall or NAT that flat out rejects peer to peer connections. TURN
# servers provide a way around this by acting as a middleman between peers, relaying all traffic sent between them.
# TURN servers are the best way to guarantee that everyone can use CrewLink. By default CrewLink comes with no TURN
# servers, so you will need to set one up. Beware that TURN servers take up a lot of bandwidth as they have to relay
# ALL traffic between peers that use them.
# turnServers:
# - url: 'turn:example.com'
# username: 'TurnUsername'
# credential: 'TurnPassword'
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
"public-ip": "^4.0.2",
"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
31 changes: 18 additions & 13 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import socketIO from 'socket.io';
import Tracer from 'tracer';
import morgan from 'morgan';
import publicIp from 'public-ip';
import peerConfig from './peer-config';

const port = parseInt(process.env.PORT || '9736');

Expand All @@ -22,22 +23,27 @@ interface Signal {
to: string;
}

app.set('view engine', 'pug')
app.use(morgan('combined'))
app.use(express.static('offsets'))
interface SetIDsPacket {
[key: string]: number
}

app.set('view engine', 'pug');
app.use(morgan('combined'));
app.use(express.static('offsets'));
let connectionCount = 0;
let address = 'loading...';

app.get('/', (req, res) => {
res.render('index', { connectionCount, address });
})

});

io.on('connection', (socket: socketIO.Socket) => {
connectionCount++;
logger.info("Total connected: %d", connectionCount);
let code: string | null = null;

socket.emit('peerConfig', peerConfig);

socket.on('join', (c: string, id: number) => {
if (typeof c !== 'string' || typeof id !== 'number') {
socket.disconnect();
Expand All @@ -48,9 +54,9 @@ io.on('connection', (socket: socketIO.Socket) => {
socket.join(code);
socket.to(code).broadcast.emit('join', socket.id, id);

let socketsInLobby = Object.keys(io.sockets.adapter.rooms[code].sockets);
let ids: any = {};
for (let s of socketsInLobby) {
const socketsInLobby = Object.keys(io.sockets.adapter.rooms[code].sockets);
const ids: SetIDsPacket = {};
for (const s of socketsInLobby) {
if (s !== socket.id)
ids[s] = playerIds.get(s);
}
Expand All @@ -65,12 +71,12 @@ io.on('connection', (socket: socketIO.Socket) => {
}
playerIds.set(socket.id, id);
socket.to(code).broadcast.emit('setId', socket.id, id);
})
});


socket.on('leave', () => {
if (code) socket.leave(code);
})
});

socket.on('signal', (signal: Signal) => {
if (typeof signal !== 'object' || !signal.data || !signal.to || typeof signal.to !== 'string') {
Expand All @@ -88,9 +94,8 @@ io.on('connection', (socket: socketIO.Socket) => {
socket.on('disconnect', () => {
connectionCount--;
logger.info("Total connected: %d", connectionCount);
})

})
});
});

server.listen(port);
(async () => {
Expand Down
24 changes: 24 additions & 0 deletions src/peer-config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import YAML from 'yaml';
import path from 'path';
import fs from 'fs';

const PEER_CONFIG_PATH = path.join(__dirname, '..', 'config', 'peer-config.yml');
const DEFAULT_PEER_CONFIG = {
forceRelayOnly: false,
stunServers: [
{
url: '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 ICE server config file. Make sure it is valid YAML.\n${err}`);
}
}

export default peerConfig;
5 changes: 5 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1267,6 +1267,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==

[email protected]:
version "0.1.2"
resolved "https://registry.yarnpkg.com/yeast/-/yeast-0.1.2.tgz#008e06d8094320c372dbc2f8ed76a0ca6c8ac419"
Expand Down