forked from neu-se/covey.town
-
Notifications
You must be signed in to change notification settings - Fork 2
/
CoveyTownController.ts
166 lines (133 loc) · 4.91 KB
/
CoveyTownController.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
import { customAlphabet, nanoid } from 'nanoid';
import { ChatData, UserLocation } from '../CoveyTypes';
import CoveyTownListener from '../types/CoveyTownListener';
import Player from '../types/Player';
import PlayerSession from '../types/PlayerSession';
import TwilioVideo from './TwilioVideo';
import IVideoClient from './IVideoClient';
const friendlyNanoID = customAlphabet('1234567890ABCDEF', 8);
/**
* The CoveyTownController implements the logic for each town: managing the various events that
* can occur (e.g. joining a town, moving, leaving a town)
*/
export default class CoveyTownController {
get capacity(): number {
return this._capacity;
}
set isPubliclyListed(value: boolean) {
this._isPubliclyListed = value;
}
get isPubliclyListed(): boolean {
return this._isPubliclyListed;
}
get townUpdatePassword(): string {
return this._townUpdatePassword;
}
get players(): Player[] {
return this._players;
}
get occupancy(): number {
return this._listeners.length;
}
get friendlyName(): string {
return this._friendlyName;
}
set friendlyName(value: string) {
this._friendlyName = value;
}
get coveyTownID(): string {
return this._coveyTownID;
}
/** The list of players currently in the town * */
private _players: Player[] = [];
/** The list of valid sessions for this town * */
private _sessions: PlayerSession[] = [];
/** The videoClient that this CoveyTown will use to provision video resources * */
private _videoClient: IVideoClient = TwilioVideo.getInstance();
/** The list of CoveyTownListeners that are subscribed to events in this town * */
private _listeners: CoveyTownListener[] = [];
private readonly _coveyTownID: string;
private _friendlyName: string;
private readonly _townUpdatePassword: string;
private _isPubliclyListed: boolean;
private _capacity: number;
constructor(friendlyName: string, isPubliclyListed: boolean) {
this._coveyTownID = (process.env.DEMO_TOWN_ID === friendlyName ? friendlyName : friendlyNanoID());
this._capacity = 50;
this._townUpdatePassword = nanoid(24);
this._isPubliclyListed = isPubliclyListed;
this._friendlyName = friendlyName;
}
/**
* Adds a player to this Covey Town, provisioning the necessary credentials for the
* player, and returning them
*
* @param newPlayer The new player to add to the town
*/
async addPlayer(newPlayer: Player): Promise<PlayerSession> {
const theSession = new PlayerSession(newPlayer);
this._sessions.push(theSession);
this._players.push(newPlayer);
// Create a video token for this user to join this town
theSession.videoToken = await this._videoClient.getTokenForTown(this._coveyTownID, newPlayer.id);
// Notify other players that this player has joined
this._listeners.forEach((listener) => listener.onPlayerJoined(newPlayer));
return theSession;
}
/**
* Destroys all data related to a player in this town.
*
* @param session PlayerSession to destroy
*/
destroySession(session: PlayerSession): void {
this._players = this._players.filter((p) => p.id !== session.player.id);
this._sessions = this._sessions.filter((s) => s.sessionToken !== session.sessionToken);
this._listeners.forEach((listener) => listener.onPlayerDisconnected(session.player));
}
/**
* Updates the location of a player within the town
* @param player Player to update location for
* @param location New location for this player
*/
updatePlayerLocation(player: Player, location: UserLocation): void {
player.updateLocation(location);
this._listeners.forEach((listener) => listener.onPlayerMoved(player));
}
/**
* Send chat data to all the listeners subscribed to the room
* @param data ChatData to be sent to all players in the room
*/
sendChatMessage(data: ChatData): void {
this._listeners.forEach((listener) => listener.onMessageSent(data));
}
/**
* Subscribe to events from this town. Callers should make sure to
* unsubscribe when they no longer want those events by calling removeTownListener
*
* @param listener New listener
*/
addTownListener(listener: CoveyTownListener): void {
this._listeners.push(listener);
}
/**
* Unsubscribe from events in this town.
*
* @param listener The listener to unsubscribe, must be a listener that was registered
* with addTownListener, or otherwise will be a no-op
*/
removeTownListener(listener: CoveyTownListener): void {
this._listeners = this._listeners.filter((v) => v !== listener);
}
/**
* Fetch a player's session based on the provided session token. Returns undefined if the
* session token is not valid.
*
* @param token
*/
getSessionByToken(token: string): PlayerSession | undefined {
return this._sessions.find((p) => p.sessionToken === token);
}
disconnectAllPlayers(): void {
this._listeners.forEach((listener) => listener.onTownDestroyed());
}
}