156 lines
4.5 KiB
TypeScript
156 lines
4.5 KiB
TypeScript
import { Quaternion, Vector3 } from 'three';
|
|
import { Packet, PacketType } from '.';
|
|
import { World } from '../gameobjects/world.object';
|
|
import { Disposable } from '../types/disposable';
|
|
import { EngineEvents, PlayerEvent } from '../types/events';
|
|
import { EventEmitter } from '../utils/events';
|
|
|
|
export class GameSocket implements Disposable {
|
|
private ws?: WebSocket;
|
|
private host?: string;
|
|
private token!: string;
|
|
private playerId!: string;
|
|
private world!: World;
|
|
private cleanUpEvents?: () => void;
|
|
constructor(private events: EventEmitter<EngineEvents>) {}
|
|
|
|
track(world: World) {
|
|
this.world = world;
|
|
}
|
|
|
|
connect(host: string, id: string, token: string) {
|
|
this.host = host;
|
|
this.playerId = id;
|
|
this.token = token;
|
|
this.ws = new WebSocket(host);
|
|
this.ws.binaryType = 'arraybuffer';
|
|
this.bindSocket(this.ws!);
|
|
console.log('connected to', this.host);
|
|
this.cleanUpEvents = this.bindEvents();
|
|
}
|
|
|
|
disconnect() {
|
|
this.ws?.close();
|
|
this.events.emit('serverDisconnect');
|
|
console.log('disconnected from', this.host);
|
|
this.cleanUpEvents?.();
|
|
}
|
|
|
|
dispose(): void {
|
|
this.cleanUpEvents?.();
|
|
}
|
|
|
|
private onOpen() {
|
|
const packet = new Packet(PacketType.AUTH)
|
|
.write(this.playerId, String)
|
|
.write(this.token, String)
|
|
.write(0, 'uint8')
|
|
.toBuffer();
|
|
this.ws?.send(packet);
|
|
}
|
|
|
|
private handlePacket(incoming: Packet) {
|
|
switch (incoming.packet) {
|
|
case PacketType.ERROR: {
|
|
const error = incoming.read(String);
|
|
alert(error);
|
|
break;
|
|
}
|
|
case PacketType.STREAM_START:
|
|
console.log('starting world loading');
|
|
break;
|
|
case PacketType.STREAM_OBJECT: {
|
|
const objectUUID = incoming.read(String);
|
|
const parentUUID = incoming.read(String);
|
|
const objectType = incoming.read(String);
|
|
const objectData = JSON.parse(incoming.read(String) as string);
|
|
this.events.emit('instance', {
|
|
type: objectType as string,
|
|
parent: parentUUID,
|
|
data: objectData,
|
|
});
|
|
break;
|
|
}
|
|
case PacketType.STREAM_TRANSFORM: {
|
|
const objectUUID = incoming.read(String)!;
|
|
const objectPos = incoming.read<Vector3>('vec3')!;
|
|
const objectQuat = incoming.read<Quaternion>('quat')!;
|
|
const objectLinvel = incoming.read<Vector3>('vec3')!;
|
|
const objectAngvel = incoming.read<Vector3>('vec3')!;
|
|
this.events.emit('serverTransform', {
|
|
object: objectUUID,
|
|
position: objectPos,
|
|
quaternion: objectQuat,
|
|
velocity: objectLinvel,
|
|
angularVelocity: objectAngvel,
|
|
});
|
|
break;
|
|
}
|
|
case PacketType.STREAM_FINISH: {
|
|
this.events.emit('loadComplete');
|
|
break;
|
|
}
|
|
case PacketType.PLAYER_LIST: {
|
|
const playerCount = incoming.read<number>('uint32')!;
|
|
const players = Array.from({ length: playerCount }, () => null).map(
|
|
() => incoming.read(String)?.split(':')
|
|
);
|
|
console.log('player list', players);
|
|
break;
|
|
}
|
|
case PacketType.PLAYER_JOIN: {
|
|
const playerId = incoming.read(String)!;
|
|
const playerName = incoming.read(String)!;
|
|
console.log('player joined', playerId, playerName);
|
|
break;
|
|
}
|
|
case PacketType.PLAYER_CHARACTER: {
|
|
const playerId = incoming.read(String)!;
|
|
const playerName = incoming.read(String)!;
|
|
const position = incoming.read<Vector3>('vec3')!;
|
|
const rotation = incoming.read<Quaternion>('quat')!;
|
|
this.events.emit('spawnCharacter', {
|
|
playerId,
|
|
playerName,
|
|
position,
|
|
rotation,
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
private bindSocket(ws: WebSocket) {
|
|
ws.addEventListener('message', (event) => {
|
|
const packet = Packet.from(Buffer.from(event.data));
|
|
this.handlePacket(packet);
|
|
});
|
|
|
|
ws.addEventListener('close', (event) => {
|
|
this.disconnect();
|
|
});
|
|
|
|
ws.addEventListener('error', (event) => {});
|
|
|
|
ws.addEventListener('open', (event) => {
|
|
this.onOpen();
|
|
});
|
|
}
|
|
|
|
private bindEvents() {
|
|
const sendPlayerEvent = (event: PlayerEvent) => {
|
|
if (this.ws?.readyState !== WebSocket.OPEN) return;
|
|
this.ws.send(
|
|
new Packet(PacketType.PLAYER_MOVEMENT)
|
|
.write(event.velocity, 'vec3')
|
|
.write(event.lookAt, 'vec3')
|
|
.toBuffer()
|
|
);
|
|
};
|
|
|
|
this.events.on('sendPlayer', sendPlayerEvent);
|
|
return () => {
|
|
this.events.removeEventListener('sendPlayer', sendPlayerEvent);
|
|
};
|
|
}
|
|
}
|