132 lines
3.7 KiB
TypeScript
132 lines
3.7 KiB
TypeScript
import { Server, Socket } from 'socket.io';
|
|
import { RequestHandler } from 'express';
|
|
import { IcyNetUser } from '../common/types/user';
|
|
import { Persistence } from './object/persistence';
|
|
import { Logger } from './object/logger';
|
|
import { ServerWorld } from './world/server-world';
|
|
import { ServerWorldLoader } from './world/server-world-loader';
|
|
import { ServerWorldManifest } from './world/server-world-manifest';
|
|
import { Player } from './object/player';
|
|
import {
|
|
ServerToClientEvents,
|
|
ClientToServerEvents,
|
|
} from '../common/types/socket';
|
|
import { InterServerEvents, SocketData } from './types/socket';
|
|
import { CompositePacket } from '../common/types/packet';
|
|
|
|
const PLACEHOLDER_USER = (socket: Socket): IcyNetUser => {
|
|
const randomName = `player-${socket.id.substring(0, 8)}`;
|
|
return {
|
|
id: Math.floor(Math.random() * 1000 + 1000),
|
|
username: randomName,
|
|
display_name: randomName,
|
|
uuid: socket.id,
|
|
accessToken: 'player',
|
|
};
|
|
};
|
|
|
|
export class Game {
|
|
private _players: Player[] = [];
|
|
private _db = new Persistence();
|
|
private _log = new Logger();
|
|
private _world!: ServerWorld;
|
|
|
|
constructor(
|
|
private io: Server<
|
|
ClientToServerEvents,
|
|
ServerToClientEvents,
|
|
InterServerEvents,
|
|
SocketData
|
|
>,
|
|
private session: RequestHandler,
|
|
) {}
|
|
|
|
async initialize() {
|
|
const manifest = await ServerWorldManifest.loadManifest();
|
|
this._world = new ServerWorld(new ServerWorldLoader(), manifest);
|
|
|
|
await this._db.initialize();
|
|
await this._world.initialize();
|
|
this._log.initialize();
|
|
|
|
this.io.use((socket, next) =>
|
|
this.session(socket.request as any, {} as any, next as any),
|
|
);
|
|
|
|
this.io.on('connection', (socket) => {
|
|
const session = (socket.request as any).session;
|
|
const user =
|
|
process.env.SKIP_LOGIN === 'true'
|
|
? PLACEHOLDER_USER(socket)
|
|
: (session?.passport?.user as IcyNetUser);
|
|
|
|
if (user && this._players.find((entry) => entry.user?.id === user.id)) {
|
|
socket.emit('error.duplicate');
|
|
return;
|
|
}
|
|
|
|
if (user) {
|
|
const player = new Player(socket, this._db, user);
|
|
this._players.push(player);
|
|
|
|
this._log.writeEvent('PlayerJoin', `<${user.display_name}:${user.id}>`);
|
|
|
|
socket.data.player = player;
|
|
player.initialize().then(() => {
|
|
// Send player list to new player
|
|
socket.emit(
|
|
'player.list',
|
|
this._players
|
|
.filter((player) => player.user)
|
|
.map((player) => ({
|
|
...player.getPublicUserProfile(),
|
|
...(player.toSave() as CompositePacket),
|
|
})),
|
|
);
|
|
});
|
|
|
|
socket.on('set.chat', (raw) => {
|
|
const message = raw.trim().substring(0, 280);
|
|
this._log.writeChat(user, message);
|
|
this.io.emit('player.chat', {
|
|
sender: player.getPublicUserProfile(),
|
|
message,
|
|
});
|
|
});
|
|
} else {
|
|
socket.emit('set.me', null);
|
|
}
|
|
|
|
socket.on('disconnect', () => {
|
|
if (user) {
|
|
const player = this._players.find(
|
|
(player) => player.user.id === user.id,
|
|
);
|
|
|
|
if (!player) {
|
|
return;
|
|
}
|
|
|
|
this.io.emit('player.leave', player.getPublicUserProfile());
|
|
|
|
this._log.writeEvent(
|
|
'PlayerLeave',
|
|
`<${user.display_name}:${user.id}>`,
|
|
);
|
|
|
|
player.dispose();
|
|
this._players.splice(this._players.indexOf(player), 1);
|
|
}
|
|
});
|
|
});
|
|
|
|
setInterval(() => {
|
|
this._players.forEach((player) => player.update(1000 / 60));
|
|
this.io.emit(
|
|
'player.update',
|
|
this._players.map((player) => player.toUpdatePacket()),
|
|
);
|
|
}, 1000 / 60);
|
|
}
|
|
}
|