Releases: colyseus/colyseus
0.15.15
Introducing Authentication Module
Documentation | Live demo | Demo Source Code
Server-side: onAuth()
changes
static onAuth(token, req)
method should be implemented asstatic
from now ononAuth(client, options, req)
as an instance method still works, but will be deprecated in the future.
This change allows validating the token earlier in the connection process, without needing an instance of the room available.
This way the auth token is read from the first matchmaking request header instead of as query param in the second WebSocket connection.
Server-side: The @colyseus/auth
module
- Uses JWT to secure communication with client SDK
- Provides authentication via express routes:
/auth/register
- user registration (email + password)/auth/login
- login (email + password)/auth/anonymous
- anonymous login/auth/userdata
- fetch user data
- Provides OAuth express routes (Thanks to @simov's work on
grant
module - a MIT-licensed "OAuth Proxy")/auth/provider/:providerId
- redirect to provider/auth/provider/:providerId/callback
- reply callback from the provider
Database interaction must be implemented by end-user
End-user should implement the following callbacks:
auth.settings.onFindUserByEmail = async (email) => {/* query user by email */}
auth.settings.onRegisterWithEmailAndPassword = async (email, password, options) => {/* insert user */}
auth.settings.onRegisterAnonymously = async (options: T) => {/* insert anonymous user */}
auth.oauth.onCallback(async (data, provider) => {/* query or insert user by OAuth provider */})
End-user may customize the following callbacks. (They come with a default implementation.)
auth.settings.onParseToken = (jwt: JwtPayload) => jwt
auth.settings.onGenerateToken = async (userdata: unknown) => await JWT.sign(userdata)
auth.settings.onHashPassword = async (password: string) => Hash.make(password)
Server-side Usage
import { auth } from "@colyseus/auth";
auth.settings.onFindUserByEmail = async (email) => {
//
// Your database query to find user by email address
//
}
auth.settings.onRegisterWithEmailAndPassword = async (email, password, options) => {
//
// Your database query to insert the user with email + password
// (The "password" is already hashed here)
//
}
// Configure Discord as OAuth provider
auth.oauth.addProvider('discord', {
key: "YOUR_DISCORD_KEY",
secret: "YOUR_DISCORD_SECRET",
scope: ['identify', 'email'],
});
auth.oauth.onCallback(async (data, provider) => {
//
// Your database query/insert. This callback must return the userdata.
// data.raw -> { token_type, access_token, expires_in, refresh_token, scope }
// data.profile -> { id, username, avatar, discriminator, public_flags, premium_type, flags, banner, accent_color, global_name, avatar_decoration_data, banner_color, mfa_enabled, locale, email }
//
});
//
// app.config.ts: bind auth express routes
//
initializeExpress: (app) => {
// bind auth + oauth express routes
app.use(auth.prefix, auth.routes());
// custom protected request
app.get("/profile", auth.middleware(), (req: Request, res) => {
res.json(req.auth);
});
},
Client-side: client.auth
API:
client.auth.registerWithEmailAndPassword(email, password, options?)
client.auth.signInWithEmailAndPassword(email, password)
client.auth.signInAnonymously(options?)
client.auth.signInWithProvider(providerName)
- open popup for OAuth providerclient.auth.sendPasswordResetEmail(email)
client.auth.getUserData()
- requests/auth/userdata
from the serverclient.auth.onChange(callback)
- define a callback that is triggered when internal auth state changes (it only triggers as a response fromclient.auth
method calls, there's no realtime subscription here!)client.auth.signOut()
- clear local auth tokenclient.auth.token
- auth token getter and setter
Client-side: client.http
API:
If client.auth.token
is set, requests from client.http
will forward it as "Authentication: Bearer {token}"
header. The match-making requests are now using client.http
as well.
client.http.get(path, options)
client.http.post(path, options)
client.http.del(path, options)
client.http.put(path, options)
Playground update (@colyseus/playground
)
The playground tool has been updated to allow customizing the client's "Auth Token".
(PRs introducing the authentication module #657, colyseus/colyseus.js#133, colyseus/docs#150)
Smaller fixes
A big thanks to @afrokick for fixing all these below 👏
0.15.13
Major changes
- Introduced an internal
Stats
module, responsible for handling theroomcount
key on Redis. - The
roomcount
hash key on Redis now holds its values separated by comma (processId
->"count,ccu"
) - The internal
Stats
module is exposed viamatchMaker.stats
, so end-user can call:matchMaker.stats.fetchAll()
->Promise<Array<{ roomCount: number; ccu: number; processId: string }>>
matchMaker.stats.getGlobalCCU()
->Promise<number>
- Added
selectProcessIdToCreateRoom
callback option toServer
constructor.
The default value for selectProcessIdToCreateRoom
behaves exactly as previous versions, but now it consumes the new matchMaker.stats.fetchAll()
API:
selectProcessIdToCreateRoom = async function (roomName: string, clientOptions: any) {
return (await matchMaker.stats.fetchAll())
.sort((p1, p2) => p1.roomCount > p2.roomCount ? 1 : -1)[0]
.processId;
}
Breaking changes
- The
roomcount
Redis key format has changed. - The
_ccu
Redis key does not exist anymore (UsematchMaker.stats.getGlobalCCU()
instead)
Smaller changes
It has been reported (by @CookedApps) that reconnection messages were flooding production logs. These logs are now filtered out on production environment.
Thanks @hunkydoryrepair and @CookedApps
0.15.10
Merged PRs from @theweiweiway & @hunkydoryrepair
0.15.9
0.15.8
Bun support
Bun support is experimental, please report any issues you may find!
# Create a new Colyseus project
bunx create-colyseus-app@latest ./my-server
# Enter the project directory
cd my-server
# Install Bun transport & Run the server
bun add @colyseus/bun-websockets
bun run src/index.ts
(Bun is a new JavaScript runtime that is fast and aims to replace Node.js.)
Breaking change for @colyseus/proxy
In order to support bun
, the internal-ip
module has been removed from the core.
If you are still using version 0.15 + @colyseus/proxy
, you should use the following code to continue using the proxy:
npm install --save internal-ip
import ip from 'internal-ip';
// ...
process.env.SELF_HOSTNAME = await ip.v4();
(Since 0.15, the usage of the proxy is not recommended anymore)
0.15.6
0.15.5
- #563 - Allow to specify types of
client.userData
andclient.auth
viaClient<UserData, AuthData>
- Fixes #555 and #532 - Zombie Rooms with multiple servers w/ RedisDriver
- Prevent
"trying to send data to inactive client"
logs as they were not helpful. @colyseus/loadtest
- add "disconnected" count below "connected" count.
Specifying types for client.userData
and client.auth
You can now specify the types of client.userData
and client.auth
via Client<UserData, AuthData>
:
import { ClientArray, Room } from "colyseus";
interface UserData {
keyboard: {[key: string]: boolean};
}
interface AuthData {
userId: number;
}
class MyRoom extends Room {
clients: ClientArray<UserData, AuthData>
onCreate() {
this.onMessage("key", (client, key) => {
// client.userData is now of type UserData
client.userData.keyboard[key] = true;
});
}
async onAuth(client, options) {
// client.auth is now of type AuthData
client.auth = await fetchUserFromDB(options);
return client.auth.userId > 0;
}
}
(If TypeScript complains on "Property 'clients' has no initializer and is not definitely assigned in the constructor", you can either declare clients!: ClientArray or set "strictPropertyInitialization":false on tsconfig (not recommended))
Updating your packages:
npm install --save @colyseus/core@latest
npm install --save @colyseus/ws-transport@latest
npm install --save @colyseus/redis-driver@latest
npm install --save-dev @colyseus/loadtest@latest
If you have these packages installed, you can also update them:
npm install --save @colyseus/uwebsockets-transport
npm install --save @colyseus/mongoose-driver
0.15
See the migration guide to 0.15! (from 0.14)
New features
- Introducing a
devMode
flag for restoring rooms and reconnecting clients during development. (see docs) - Introducing a new scalability option that does not rely on
@colyseus/proxy
. (see docs) - Switched to a faster MessagePack implementation (
msgpackr
- #528, see benchmarks) - New Room's
onBeforePatch
lifecycle hook. (#385) - Schema: single property callback is now available on all SDKs (see docs)
- Possibility to access a client instance directly via its
sessionId
(#443) - Support for custom loggers (via
logger:
Server option) (#442) - Introduced log flag for incoming and outgoing messages (
DEBUG=colyseus:message
) (#465, see docs)
Breaking changes
@colyseus/arena
package has been renamed to@colyseus/tools
client.reconnect()
vulnerability fixed & API slightly changedallowReconnection()
: second argument is now mandatory@colyseus/loadtest
has been reworked!@colyseus/command
typings update- Schema's
.triggerAll()
has been deprecated. - Schema callbacks API change
- Schema's
onChange
behaviour change MapSchema
is now strict on property accessors
Bug fixes / Improvements
MongooseDriver
is not the default recommendation anymore. UseRedisDriver
instead."redis"
module has been replaced by"ioredis"
for cluster support on bothRedisPresence
andRedisDriver
(#452)- Some room properties are now fully private (#441)
- Fixed an issue where matchmaking filter returning all available rooms when filtering criteria is
""
(empty string) ornull
when usinggameServer.filterBy()
option. (#342)
0.14.18
Migrating guide to 0.14.18 https://docs.colyseus.io/migrating/0.14.18/
Breaking changes:
- The
MongooseDriver
should now be imported from@colyseus/mongoose-driver
- That's it! If you happen to face any issues please let us know! 🙏
This is a maintenance update - now the colyseus
package has been broken down into isolated modules to avoid tying specific modules too hard with the framework.
Examples of this are the new RedisDriver
(thanks @vitalyrotari) and the new uWebSocketsTransport
transport option!
Here's the list of current packages in the colyseus/colyseus repository now:
colyseus
- aggregates previously existing features through their respective modules@colyseus/core
- all framework abstractions and features@colyseus/ws-transport
- default WebSocket transport (throughws
module)@colyseus/redis-presence
- provides RedisPresence@colyseus/mongoose-driver
- provides MongooseDriver
@colyseus/uwebsockets-transport
- New transport option!, usesuWebSockets.js
instead ofws
🥳@colyseus/redis-driver
- #412, thanks @vitalyrotari!@colyseus/monitor
- moved from its own repository into this one@colyseus/arena
- moved from its own repository into this one
0.14.0
Check out the migration guide from 0.13 to 0.14 here.
The @colyseus/schema has received a major restructuring internally, here's what has changed at a glance:
- Instance-sharing is now supported! You may use the same reference of a schema instance multiple times for synchronization without having to .clone() it.
- Better
@filter()
implementation, and the addition of@filterChildren()
. Although filters are still not recommended for fast-paced games, it should serve turn-based scenarios pretty well! - The
MapSchema
now uses a realMap
underneath instead of plain objects. - It is not recommended to implement
constructor()
inside your schemas. Use.assign({ ... })
instead.
Besides the schema changes, here are some small changes in the framework itself:
simulateLatency()
: this utility has been included to help you simulate networking delays during development.broadcastPatch()
: this internal method has been exposed, so if you wanna have control over when to send state patches to connected clients, now you can!- Bugfix: messages from "disconnecting" clients are now being dropped. (During some disconnection scenarios, the "client" instance of a particular "sessionId" was already removed from the server, but messages from that "sessionId" were still arriving at the server.)
The website was re-branded to focus on the maintenance and development of the framework, as well as giving more exposure to the Discord community!
Thank you all for the support! ❤