This commit is contained in:
Evert Prants 2022-04-10 09:52:50 +03:00
parent 43c1fe0f96
commit 057425695e
Signed by: evert
GPG Key ID: 1688DA83D222D0B5
7 changed files with 120 additions and 24 deletions

Binary file not shown.

View File

@ -1,7 +1,8 @@
import e from 'express'; import e from 'express';
import { Socket } from 'socket.io-client'; import { Socket } from 'socket.io-client';
import { Color } from 'three';
import { isMobileOrTablet } from '../common/helper'; import { isMobileOrTablet } from '../common/helper';
import { CompositePacket } from '../common/types/packet'; import { CharacterPacket, CompositePacket } from '../common/types/packet';
import { IcyNetUser } from '../common/types/user'; import { IcyNetUser } from '../common/types/user';
import { ThirdPersonCamera } from './object/camera'; import { ThirdPersonCamera } from './object/camera';
import { Chat } from './object/chat'; import { Chat } from './object/chat';
@ -19,6 +20,7 @@ export class Game {
public thirdPersonCamera!: ThirdPersonCamera; public thirdPersonCamera!: ThirdPersonCamera;
public joystick!: Joystick; public joystick!: Joystick;
public chat!: Chat; public chat!: Chat;
private character: CharacterPacket = {};
private party: string[] = []; private party: string[] = [];
public renderer = new Renderer(); public renderer = new Renderer();
@ -43,6 +45,11 @@ export class Game {
this.party = (localStorage.getItem('party')?.split('|') || []).filter( this.party = (localStorage.getItem('party')?.split('|') || []).filter(
(item) => item, (item) => item,
); );
const colorGet = localStorage.getItem('color');
if (colorGet) {
this.character.color = colorGet;
}
// end of // end of
this.chat.registerSendFunction((message) => { this.chat.registerSendFunction((message) => {
@ -89,6 +96,7 @@ export class Game {
this.me = user; this.me = user;
const player = Player.fromUser(user, this.renderer.scene); const player = Player.fromUser(user, this.renderer.scene);
player.setCharacter(this.character);
this.players.push(player); this.players.push(player);
this.player = player; this.player = player;
this.thirdPersonCamera = new ThirdPersonCamera( this.thirdPersonCamera = new ThirdPersonCamera(
@ -104,6 +112,8 @@ export class Game {
if (isMobileOrTablet()) { if (isMobileOrTablet()) {
this.joystick.show(); this.joystick.show();
} }
this.socket.emit('character', this.character);
}); });
this.socket.on('playerjoin', (user) => { this.socket.on('playerjoin', (user) => {
@ -119,6 +129,7 @@ export class Game {
const findPlayer = this.players.find((item) => item.user.id === user.id); const findPlayer = this.players.find((item) => item.user.id === user.id);
if (findPlayer) { if (findPlayer) {
this.renderer.scene.remove(findPlayer.container); this.renderer.scene.remove(findPlayer.container);
findPlayer.dispose();
this.players.splice(this.players.indexOf(findPlayer), 1); this.players.splice(this.players.indexOf(findPlayer), 1);
} }
}); });
@ -150,6 +161,17 @@ export class Game {
}); });
}); });
this.socket.on('playercharacter', (data) => {
const player = this.players.find((player) => player.user.id === data.id);
if (
player &&
player instanceof PlayerEntity &&
player.user.id !== this.me.id
) {
player.setCharacter(data);
}
});
this.socket.on('chat', (event) => { this.socket.on('chat', (event) => {
const player = this.players.find( const player = this.players.find(
(item) => item.user.id === event.sender.id, (item) => item.user.id === event.sender.id,
@ -167,32 +189,51 @@ export class Game {
} }
private experimentalPlayerCmd(message: string, sender: IcyNetUser) { private experimentalPlayerCmd(message: string, sender: IcyNetUser) {
if (message.startsWith('!party') && this.me.id === sender.id) { if (this.me.id === sender.id) {
const array = message.split(' '); if (message.startsWith('!color')) {
const name = array.slice(2).join(' '); const [cmd, color] = message.split(' ');
try {
const colorr = new Color(color);
if (!colorr) {
throw 'invalid';
}
} catch (e: any) {
this.chat.addMessage('Invalid color.');
return;
}
if (array[1] === 'join') { this.player.setColor(color);
this.party.push(name); this.socket.emit('character', { color });
this.chat.addMessage(`Joined party of user "${name}".`); localStorage.setItem('color', color);
} }
if (array[1] === 'leave') { if (message.startsWith('!party')) {
this.party.splice(this.party.indexOf(name), 1); const array = message.split(' ');
this.chat.addMessage(`Left party of user "${name}".`); const name = array.slice(2).join(' ');
}
if (array[1] === 'clear') { if (array[1] === 'join') {
this.party.length = 0; this.party.push(name);
this.chat.addMessage('Cleared party list.'); this.chat.addMessage(`Joined party of user "${name}".`);
} }
if (array[1] === 'list') { if (array[1] === 'leave') {
this.chat.addMessage( this.party.splice(this.party.indexOf(name), 1);
`You have joined the watch party of: ${this.party.join(', ')}`, this.chat.addMessage(`Left party of user "${name}".`);
); }
}
localStorage.setItem('party', this.party.join('|')); if (array[1] === 'clear') {
this.party.length = 0;
this.chat.addMessage('Cleared party list.');
}
if (array[1] === 'list') {
this.chat.addMessage(
`You have joined the watch party of: ${this.party.join(', ')}`,
);
}
localStorage.setItem('party', this.party.join('|'));
}
} }
if ( if (

View File

@ -2,6 +2,7 @@ import { IcyNetUser } from '../../common/types/user';
import * as THREE from 'three'; import * as THREE from 'three';
import { PonyEntity } from './pony'; import { PonyEntity } from './pony';
import { import {
CharacterPacket,
FullStatePacket, FullStatePacket,
PositionUpdatePacket, PositionUpdatePacket,
} from '../../common/types/packet'; } from '../../common/types/packet';
@ -101,13 +102,17 @@ export class PlayerEntity extends PonyEntity {
); );
} }
public addUncommittedChanges(packet: PositionUpdatePacket) { public addUncommittedChanges(packet: FullStatePacket) {
const appendix = { ...packet, time: 0.1 }; const appendix = { ...packet, time: 0.1 };
this._updateQueue.push(appendix); this._updateQueue.push(appendix);
if (!this._targetFrame) { if (!this._targetFrame) {
this.setFromPacket(packet); this.setFromPacket(packet);
this._targetFrame = appendix; this._targetFrame = appendix;
} }
if (packet.character) {
this.setCharacter(packet.character);
}
} }
public update(dt: number) { public update(dt: number) {

View File

@ -1,5 +1,6 @@
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'; import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader'; import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader';
import { Color, Mesh, MeshStandardMaterial } from 'three';
// Instantiate a loader // Instantiate a loader
const loader = new GLTFLoader(); const loader = new GLTFLoader();
@ -26,6 +27,11 @@ class PonyModel {
// temp: disable frustum culling for eyes // temp: disable frustum culling for eyes
this.ponyModel.children[0].children[2].frustumCulled = false; this.ponyModel.children[0].children[2].frustumCulled = false;
// (
// (this.ponyModel.children[0].children[1] as Mesh)
// .material as MeshStandardMaterial
// ).color = new Color(0xffffff);
resolve(gltf.scene); resolve(gltf.scene);
// gltf.animations; // Array<THREE.AnimationClip> // gltf.animations; // Array<THREE.AnimationClip>

View File

@ -1,9 +1,10 @@
import * as SkeletonUtils from 'three/examples/jsm/utils/SkeletonUtils'; import * as SkeletonUtils from 'three/examples/jsm/utils/SkeletonUtils';
import * as THREE from 'three'; import * as THREE from 'three';
import modelLoaderInstance from './pony-loader'; import modelLoaderInstance from './pony-loader';
import { FullStatePacket } from '../../common/types/packet'; import { CharacterPacket, FullStatePacket } from '../../common/types/packet';
import { NameTag } from './nametag'; import { NameTag } from './nametag';
import { CanvasUtils } from './canvas-utils'; import { CanvasUtils } from './canvas-utils';
import { Mesh, MeshStandardMaterial, Color } from 'three';
const nameTagBuilder = new CanvasUtils({ const nameTagBuilder = new CanvasUtils({
fill: false, fill: false,
@ -19,6 +20,7 @@ export class PonyEntity {
public mixer!: THREE.AnimationMixer; public mixer!: THREE.AnimationMixer;
public container!: THREE.Object3D; public container!: THREE.Object3D;
public model!: THREE.Object3D; public model!: THREE.Object3D;
public material!: THREE.MeshStandardMaterial;
public walkAnimationState = 0; public walkAnimationState = 0;
public idleAction: THREE.AnimationAction; public idleAction: THREE.AnimationAction;
public walkAction: THREE.AnimationAction; public walkAction: THREE.AnimationAction;
@ -27,6 +29,11 @@ export class PonyEntity {
initialize() { initialize() {
this.model = (SkeletonUtils as any).clone(modelLoaderInstance.ponyModel); this.model = (SkeletonUtils as any).clone(modelLoaderInstance.ponyModel);
this.material = (
(this.model.children[0].children[1] as Mesh)
.material as MeshStandardMaterial
).clone();
(this.model.children[0].children[1] as Mesh).material = this.material;
this.mixer = new THREE.AnimationMixer(this.model); this.mixer = new THREE.AnimationMixer(this.model);
this.idleAction = this.mixer.clipAction(modelLoaderInstance.animations[0]); this.idleAction = this.mixer.clipAction(modelLoaderInstance.animations[0]);
this.walkAction = this.mixer.clipAction(modelLoaderInstance.animations[2]); this.walkAction = this.mixer.clipAction(modelLoaderInstance.animations[2]);
@ -47,6 +54,12 @@ export class PonyEntity {
this.mixer.update(dt); this.mixer.update(dt);
} }
dispose() {
this.model = null;
this.material.dispose();
this.nameTag?.dispose();
}
public addNameTag(name: string) { public addNameTag(name: string) {
this.nameTag = new NameTag(nameTagBuilder, name); this.nameTag = new NameTag(nameTagBuilder, name);
this.nameTag.tag.position.set(0, 1.8, 0.5); this.nameTag.tag.position.set(0, 1.8, 0.5);
@ -68,4 +81,14 @@ export class PonyEntity {
this.walkAction.crossFadeTo(this.idleAction.reset(), 0.5, false).play(); this.walkAction.crossFadeTo(this.idleAction.reset(), 0.5, false).play();
} }
} }
public setColor(color: number | string) {
this.material.color = new Color(color);
}
public setCharacter(packet: CharacterPacket) {
if (packet.color) {
this.setColor(packet.color);
}
}
} }

View File

@ -14,6 +14,11 @@ export interface FullStatePacket {
position?: number[]; position?: number[];
rotation?: (number | string)[]; rotation?: (number | string)[];
animState?: number; animState?: number;
character?: CharacterPacket;
}
export interface CharacterPacket {
color?: number | string;
} }
export interface CompositePacket extends IcyNetUser, FullStatePacket {} export interface CompositePacket extends IcyNetUser, FullStatePacket {}

View File

@ -1,7 +1,10 @@
import { Server, Socket } from 'socket.io'; import { Server, Socket } from 'socket.io';
import { RequestHandler } from 'express'; import { RequestHandler } from 'express';
import { IcyNetUser } from '../../common/types/user'; import { IcyNetUser } from '../../common/types/user';
import { PositionUpdatePacket } from '../../common/types/packet'; import {
CharacterPacket,
PositionUpdatePacket,
} from '../../common/types/packet';
const PLACEHOLDER_USER = (socket: Socket): IcyNetUser => { const PLACEHOLDER_USER = (socket: Socket): IcyNetUser => {
const randomName = `player-${socket.id.substring(0, 8)}`; const randomName = `player-${socket.id.substring(0, 8)}`;
@ -55,6 +58,7 @@ export class Game {
position: [0, 0, 0], position: [0, 0, 0],
rotation: [0, 0, 0, 'XYZ'], rotation: [0, 0, 0, 'XYZ'],
animState: 0, animState: 0,
character: {},
}; };
socket.emit( socket.emit(
@ -78,6 +82,18 @@ export class Game {
} }
}); });
socket.on('character', (info: CharacterPacket) => {
socket.data.playerinfo.character = {
...socket.data.playerinfo.character,
...info,
};
this.io.emit('playercharacter', {
id: socket.data.user.id,
...socket.data.playerinfo.character,
});
});
socket.on('chat-send', (raw) => { socket.on('chat-send', (raw) => {
const message = raw.trim().substring(0, 260); const message = raw.trim().substring(0, 260);
this.io.emit('chat', { sender: publicUserInfo, message }); this.io.emit('chat', { sender: publicUserInfo, message });