1
0
forked from noxious/server

Work for teleports

This commit is contained in:
Dennis Postma 2024-08-23 20:16:06 +02:00
parent c909bc4aa7
commit e0b376cb83
4 changed files with 172 additions and 99 deletions

View File

@ -1,93 +1,156 @@
import { Server } from 'socket.io' import { Server } from 'socket.io';
import { TSocket, ExtendedCharacter } from '../../utilities/types' import { TSocket, ExtendedCharacter } from '../../utilities/types';
import ZoneManager from '../../managers/zoneManager' import ZoneManager from '../../managers/zoneManager';
import prisma from '../../utilities/prisma' import prisma from '../../utilities/prisma';
import { AStar } from '../../utilities/player/aStar' import { AStar } from '../../utilities/player/aStar';
import Rotation from '../../utilities/player/rotation' import Rotation from '../../utilities/player/rotation';
import ZoneRepository from '../../repositories/zoneRepository';
import { Character } from '@prisma/client';
const moveTokens = new Map<number, symbol>() const moveTokens = new Map<number, symbol>();
export default function setupCharacterMove(socket: TSocket, io: Server) { export default function setupCharacterMove(socket: TSocket, io: Server) {
socket.on('character:move', async ({ positionX, positionY }: { positionX: number; positionY: number }) => { socket.on('character:initMove', handleCharacterMove(socket, io));
const { character } = socket
if (!character) return console.error('character:move error', 'Character not found')
moveTokens.set(character.id, Symbol('moveToken'))
const grid = await ZoneManager.getGrid(character.zoneId)
if (!grid?.length) return console.error('character:move error', 'Grid not found or empty')
const start = { x: Math.floor(character.positionX), y: Math.floor(character.positionY) }
const end = { x: Math.floor(positionX), y: Math.floor(positionY) }
if (isObstacle(end, grid)) return socket.emit('character:moveError', 'Destination is an obstacle')
const path = AStar.findPath(start, end, grid)
if (!path.length) return socket.emit('character:moveError', 'No valid path found')
character.isMoving = true
io.in(character.zoneId.toString()).emit('character:moved', character)
moveAlongPath(socket, io, path, grid).catch(console.error)
})
} }
const handleCharacterMove = (socket: TSocket, io: Server) => async ({ positionX, positionY }: { positionX: number; positionY: number }) => {
const { character } = socket;
if (!character) return console.error('character:move error', 'Character not found');
const grid = await ZoneManager.getGrid(character.zoneId);
if (!grid?.length) return console.error('character:move error', 'Grid not found or empty');
const start = { x: Math.floor(character.positionX), y: Math.floor(character.positionY) };
const end = { x: Math.floor(positionX), y: Math.floor(positionY) };
if (isObstacle(end, grid)) return socket.emit('character:moveError', 'Destination is an obstacle');
const path = AStar.findPath(start, end, grid);
if (!path.length) return socket.emit('character:moveError', 'No valid path found');
moveTokens.set(character.id, Symbol('moveToken'));
character.isMoving = true;
io.in(character.zoneId.toString()).emit('character:move', character);
moveAlongPath(socket, io, path, grid).catch(console.error);
};
async function moveAlongPath(socket: TSocket, io: Server, path: Array<{ x: number; y: number }>, grid: number[][]) { async function moveAlongPath(socket: TSocket, io: Server, path: Array<{ x: number; y: number }>, grid: number[][]) {
const { character } = socket const { character } = socket;
if (!character) return if (!character) return;
const moveToken = moveTokens.get(character.id) const moveToken = moveTokens.get(character.id);
const stepDuration = 250 const stepDuration = 250;
const updateInterval = 50 const updateInterval = 50;
for (let i = 0; i < path.length - 1; i++) { for (let i = 0; i < path.length - 1; i++) {
const startTime = Date.now() const startTime = Date.now();
const start = path[i] const start = path[i];
const end = path[i + 1] const end = path[i + 1];
while (Date.now() - startTime < stepDuration) { while (Date.now() - startTime < stepDuration) {
if (moveTokens.get(character.id) !== moveToken) return if (moveTokens.get(character.id) !== moveToken) return;
const progress = (Date.now() - startTime) / stepDuration const progress = (Date.now() - startTime) / stepDuration;
const current = interpolatePosition(start, end, progress) const current = interpolatePosition(start, end, progress);
if (isObstacle(current, grid)) { if (isObstacle(current, grid)) {
// If obstacle encountered, stop at the last valid position await updateCharacterPosition(character, start, Rotation.calculate(start.x, start.y, end.x, end.y), socket, io);
await updateCharacter(character, start, Rotation.calculate(start.x, start.y, end.x, end.y)) return;
io.in(character.zoneId.toString()).emit('character:moved', character)
return
} }
await updateCharacter(character, current, Rotation.calculate(start.x, start.y, end.x, end.y)) const tp = await prisma.zoneEventTile.findFirst({
io.in(character.zoneId.toString()).emit('character:moved', character) where: { zoneId: character.zoneId, type: 'TELEPORT', positionX: current.x, positionY: current.y },
await new Promise((resolve) => setTimeout(resolve, updateInterval)) include: { teleport: true }
});
if (tp?.teleport) {
await handleTeleport(socket, io, character, tp.teleport, start, end);
return;
}
await updateCharacterPosition(character, current, Rotation.calculate(start.x, start.y, end.x, end.y), socket, io);
await new Promise(resolve => setTimeout(resolve, updateInterval));
} }
} }
// Ensure the character reaches the exact final position
if (moveTokens.get(character.id) === moveToken) { if (moveTokens.get(character.id) === moveToken) {
const finalPosition = path[path.length - 1] await updateCharacterPosition(character, path[path.length - 1], character.rotation, socket, io);
await updateCharacter(character, finalPosition, character.rotation) character.isMoving = false;
character.isMoving = false moveTokens.delete(character.id);
moveTokens.delete(character.id)
io.in(character.zoneId.toString()).emit('character:moved', character)
} }
} }
async function handleTeleport(socket: TSocket, io: Server, character: ExtendedCharacter, teleport: any, start: { x: number; y: number }, end: { x: number; y: number }) {
if (teleport.toZoneId === character.zoneId) return;
const zone = await ZoneRepository.getById(teleport.toZoneId);
if (!zone) return;
character.isMoving = false;
character.zoneId = teleport.toZoneId;
moveTokens.delete(character.id);
socket.leave(character.zoneId.toString());
socket.join(teleport.toZoneId.toString());
await updateCharacterPosition(
character,
{ x: teleport.toPositionX, y: teleport.toPositionY },
Rotation.calculate(start.x, start.y, end.x, end.y),
socket,
io,
teleport.toZoneId
);
}
async function updateCharacterPosition(
character: ExtendedCharacter,
position: { x: number; y: number },
rotation: number,
socket: TSocket,
io: Server,
newZoneId?: number
) {
const oldZoneId = character.zoneId;
Object.assign(character, {
positionX: position.x,
positionY: position.y,
rotation,
zoneId: newZoneId || character.zoneId
});
if (newZoneId && newZoneId !== oldZoneId) {
io.to(oldZoneId.toString()).emit('zone:character:leave', character);
io.to(newZoneId.toString()).emit('zone:character:join', character);
ZoneManager.removeCharacterFromZone(oldZoneId, character as Character);
ZoneManager.addCharacterToZone(newZoneId, character as Character);
} else {
ZoneManager.updateCharacterInZone(character.zoneId, character);
}
await prisma.character.update({
where: { id: character.id },
data: {
positionX: position.x,
positionY: position.y,
rotation,
zoneId: character.zoneId
}
});
io.in(character.zoneId.toString()).emit('character:move', character);
socket.emit('character:dataUpdated', character);
}
const interpolatePosition = (start: { x: number; y: number }, end: { x: number; y: number }, progress: number) => ({ const interpolatePosition = (start: { x: number; y: number }, end: { x: number; y: number }, progress: number) => ({
x: start.x + (end.x - start.x) * progress, x: start.x + (end.x - start.x) * progress,
y: start.y + (end.y - start.y) * progress y: start.y + (end.y - start.y) * progress
}) });
const isObstacle = ({ x, y }: { x: number; y: number }, grid: number[][]) => { const isObstacle = ({ x, y }: { x: number; y: number }, grid: number[][]) => {
const gridX = Math.floor(x) const gridX = Math.floor(x);
const gridY = Math.floor(y) const gridY = Math.floor(y);
return grid[gridY]?.[gridX] === 1 || grid[gridY]?.[Math.ceil(x)] === 1 || grid[Math.ceil(y)]?.[gridX] === 1 || grid[Math.ceil(y)]?.[Math.ceil(x)] === 1 return grid[gridY]?.[gridX] === 1 || grid[gridY]?.[Math.ceil(x)] === 1 || grid[Math.ceil(y)]?.[gridX] === 1 || grid[Math.ceil(y)]?.[Math.ceil(x)] === 1;
} };
async function updateCharacter(character: ExtendedCharacter, { x, y }: { x: number; y: number }, rotation: number) {
Object.assign(character, { positionX: x, positionY: y, rotation })
ZoneManager.updateCharacterInZone(character.zoneId, character)
await prisma.character.update({
where: { id: character.id },
data: { positionX: x, positionY: y, rotation }
})
}

View File

@ -32,8 +32,6 @@ export default function (socket: TSocket, io: Server) {
socket.leave(zone.id.toString()) socket.leave(zone.id.toString())
socket.emit('character:zone:unload')
// let other clients know of new character // let other clients know of new character
io.to(zone.id.toString()).emit('zone:character:leave', socket.character) io.to(zone.id.toString()).emit('zone:character:leave', socket.character)

View File

@ -3,6 +3,7 @@ import { TSocket } from '../../utilities/types'
import ZoneRepository from '../../repositories/zoneRepository' import ZoneRepository from '../../repositories/zoneRepository'
import ZoneManager from '../../managers/zoneManager' import ZoneManager from '../../managers/zoneManager'
import { Character, Zone } from '@prisma/client' import { Character, Zone } from '@prisma/client'
import logger from '../../utilities/logger'
interface IPayload { interface IPayload {
zoneId: number zoneId: number
@ -20,8 +21,11 @@ interface IResponse {
*/ */
export default function (socket: TSocket, io: Server) { export default function (socket: TSocket, io: Server) {
socket.on('character:zone:request', async (data: IPayload, callback: (response: IResponse) => void) => { socket.on('character:zone:request', async (data: IPayload, callback: (response: IResponse) => void) => {
try {
console.log(`---User ${socket.character?.id} has requested zone.`) console.log(`---User ${socket.character?.id} has requested zone.`)
if (!socket.character) return;
if (!data.zoneId) { if (!data.zoneId) {
console.log(`---Zone id not provided.`) console.log(`---Zone id not provided.`)
return return
@ -34,15 +38,25 @@ export default function (socket: TSocket, io: Server) {
return return
} }
socket.join(zone.id.toString()) if (socket.character?.zoneId) {
socket.leave(socket.character.zoneId.toString())
io.to(socket.character.zoneId.toString()).emit('zone:character:leave', socket.character)
}
// send over zone and characters to socket socket.character.zoneId = zone.id
callback({ zone, characters: ZoneManager.getCharactersInZone(zone.id) }) socket.join(zone.id.toString())
// let other clients know of new character // let other clients know of new character
io.to(zone.id.toString()).emit('zone:character:join', socket.character) io.to(zone.id.toString()).emit('zone:character:join', socket.character)
// add character to zone manager // add character to zone manager
ZoneManager.addCharacterToZone(zone.id, socket.character as Character) ZoneManager.addCharacterToZone(zone.id, socket.character as Character)
// send over zone and characters to socket
callback({ zone, characters: ZoneManager.getCharactersInZone(zone.id) })
} catch (error: any) {
console.log(`Error requesting zone: ${error.message}`)
logger.error(`Error requesting zone: ${error.message}`)
}
}) })
} }

View File

@ -73,8 +73,7 @@ export default function (socket: TSocket, io: Server) {
type: zoneEventTile.type, type: zoneEventTile.type,
positionX: zoneEventTile.positionX, positionX: zoneEventTile.positionX,
positionY: zoneEventTile.positionY, positionY: zoneEventTile.positionY,
...(zoneEventTile.type === 'TELEPORT' && zoneEventTile.teleport ...(zoneEventTile.type === 'TELEPORT' && zoneEventTile.teleport ? {
? {
teleport: { teleport: {
create: { create: {
toZoneId: zoneEventTile.teleport.toZoneId, toZoneId: zoneEventTile.teleport.toZoneId,
@ -82,8 +81,7 @@ export default function (socket: TSocket, io: Server) {
toPositionY: zoneEventTile.teleport.toPositionY toPositionY: zoneEventTile.teleport.toPositionY
} }
} }
} } : {})
: {})
})) }))
}, },
zoneObjects: { zoneObjects: {