починка тг

This commit is contained in:
2025-08-31 15:08:38 +03:00
parent 4f1187915b
commit 950f29cea6
3 changed files with 4893 additions and 4889 deletions

15
db1.js
View File

@@ -2,15 +2,22 @@ require('dotenv').config();
const { Pool } = require('pg'); const { Pool } = require('pg');
const connectionString = const connectionString =
process.env.DATABASE_URL_VIRTUAL_WORLD || process.env.DATABASE_URL; process.env.DATABASE_URL_VIRTUAL_WORLD;
console.log('<27><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>: ', connectionString);
const virtualWorldPool = new Pool({ const virtualWorldPool = new Pool({
connectionString, connectionString,
ssl: false ssl: false
}); });
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
virtualWorldPool.on('error', (err) => {
console.error('<27><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>:', err);
});
virtualWorldPool.on('connect', () => {
console.log('<27><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>');
});
module.exports = { module.exports = {
virtualWorldPool: {
query: (text, params) => virtualWorldPool.query(text, params) query: (text, params) => virtualWorldPool.query(text, params)
}
}; };

View File

@@ -1,4 +1,4 @@
let dotenv, express, db, Economy, GameTime, pathLib, fs, virtualWorldPool; let dotenv, express, db, Economy, GameTime, pathLib, fs, virtualWorldPool, new_quest_Base;
try { try {
dotenv = require('dotenv').config(); dotenv = require('dotenv').config();
console.log('dotenv успешно импортирован'); console.log('dotenv успешно импортирован');
@@ -24,6 +24,25 @@ try {
console.error('Ошибка при импорте express:', e); console.error('Ошибка при импорте express:', e);
throw e; throw e;
} }
try {
virtualWorldPool = require('./db1');
console.log('db1 - virtualWorld - успешно импротирован');
}
catch (e) {
console.error('Ошибка при импорте db1 - virtual_World:', e);
throw e;
}
try {
new_quest_Base = require('./db2');
console.log('db2 - new_quest_Base - успешно импортирован');
}
catch (e) {
console.error('Ошибка при импорте db2 - new_quest_Base: ', e);
throw e;
}
try { try {
db = require('./db'); db = require('./db');
console.log('db успешно импортирован'); console.log('db успешно импортирован');
@@ -424,10 +443,9 @@ app.get('/api/users', authenticate, async (req, res) => {
app.get('/api/messages/:contactId', authenticate, async (req, res) => { app.get('/api/messages/:contactId', authenticate, async (req, res) => {
const userId = req.user.id; const userId = req.user.id;
const contactId = parseInt(req.params.contactId, 10); const contactId = parseInt(req.params.contactId, 10);
const pool = (typeof virtualWorldPool !== 'undefined' && virtualWorldPool) ? virtualWorldPool : db;
try { try {
// Ensure table exists // Ensure table exists
await pool.query(` await virtualWorldPool.query(`
CREATE TABLE IF NOT EXISTS messages ( CREATE TABLE IF NOT EXISTS messages (
id SERIAL PRIMARY KEY, id SERIAL PRIMARY KEY,
sender_id INT NOT NULL, sender_id INT NOT NULL,
@@ -445,7 +463,7 @@ app.get('/api/messages/:contactId', authenticate, async (req, res) => {
WHERE (sender_id = $1 AND receiver_id = $2) WHERE (sender_id = $1 AND receiver_id = $2)
OR (sender_id = $2 AND receiver_id = $1) OR (sender_id = $2 AND receiver_id = $1)
ORDER BY created_at ASC`; ORDER BY created_at ASC`;
const messagesRes = await pool.query(sql, [userId, contactId]); const messagesRes = await virtualWorldPool.query(sql, [userId, contactId]);
res.json(messagesRes.rows); res.json(messagesRes.rows);
} catch (err) { } catch (err) {
console.error('[GET /api/messages/:contactId] error:', err); console.error('[GET /api/messages/:contactId] error:', err);
@@ -457,7 +475,6 @@ app.post('/api/messages/send', authenticate, async (req, res) => {
const senderId = req.user.id; const senderId = req.user.id;
const { receiverId, message } = req.body || {}; const { receiverId, message } = req.body || {};
const recvId = parseInt(receiverId, 10); const recvId = parseInt(receiverId, 10);
const pool = (typeof virtualWorldPool !== 'undefined' && virtualWorldPool) ? virtualWorldPool : db;
console.log('[POST /api/messages/send] sender:', senderId, 'receiver:', recvId); console.log('[POST /api/messages/send] sender:', senderId, 'receiver:', recvId);
try { try {
const receiverCheck = await db.query('SELECT id FROM users WHERE id = $1', [recvId]); const receiverCheck = await db.query('SELECT id FROM users WHERE id = $1', [recvId]);
@@ -465,7 +482,7 @@ app.post('/api/messages/send', authenticate, async (req, res) => {
return res.status(404).json({ error: 'Пользователь не найден' }); return res.status(404).json({ error: 'Пользователь не найден' });
} }
try { try {
await pool.query(` await virtualWorldPool.query(`
CREATE TABLE IF NOT EXISTS messages ( CREATE TABLE IF NOT EXISTS messages (
id SERIAL PRIMARY KEY, id SERIAL PRIMARY KEY,
sender_id INT NOT NULL, sender_id INT NOT NULL,
@@ -478,7 +495,7 @@ app.post('/api/messages/send', authenticate, async (req, res) => {
console.warn('[POST /api/messages/send] ensure table failed:', e.message); console.warn('[POST /api/messages/send] ensure table failed:', e.message);
} }
const result = await pool.query( const result = await virtualWorldPool.query(
`INSERT INTO messages (sender_id, receiver_id, message, created_at) `INSERT INTO messages (sender_id, receiver_id, message, created_at)
VALUES ($1, $2, $3, NOW()) VALUES ($1, $2, $3, NOW())
RETURNING id, created_at, is_read`, RETURNING id, created_at, is_read`,
@@ -505,9 +522,8 @@ app.post('/api/messages/send', authenticate, async (req, res) => {
app.get('/api/messages', authenticate, async (req, res) => { app.get('/api/messages', authenticate, async (req, res) => {
const userId = req.user.id; const userId = req.user.id;
const pool = (typeof virtualWorldPool !== 'undefined' && virtualWorldPool) ? virtualWorldPool : db;
try { try {
await pool.query(` await virtualWorldPool.query(`
CREATE TABLE IF NOT EXISTS messages ( CREATE TABLE IF NOT EXISTS messages (
id SERIAL PRIMARY KEY, id SERIAL PRIMARY KEY,
sender_id INT NOT NULL, sender_id INT NOT NULL,
@@ -520,7 +536,7 @@ app.get('/api/messages', authenticate, async (req, res) => {
console.warn('[GET /api/messages] ensure table failed:', e.message); console.warn('[GET /api/messages] ensure table failed:', e.message);
} }
try { try {
const messagesRes = await pool.query( const messagesRes = await virtualWorldPool.query(
`SELECT * FROM messages `SELECT * FROM messages
WHERE sender_id = $1 OR receiver_id = $1 WHERE sender_id = $1 OR receiver_id = $1
ORDER BY created_at DESC`, ORDER BY created_at DESC`,
@@ -531,7 +547,7 @@ app.get('/api/messages', authenticate, async (req, res) => {
} }
const userIds = new Set(); const userIds = new Set();
messagesRes.rows.forEach(msg => { userIds.add(msg.sender_id); userIds.add(msg.receiver_id); }); messagesRes.rows.forEach(msg => { userIds.add(msg.sender_id); userIds.add(msg.receiver_id); });
const usersRes = await db.query( const usersRes = await virtualWorldPool.query(
`SELECT id, first_name, last_name, avatar_url FROM users WHERE id = ANY($1)`, `SELECT id, first_name, last_name, avatar_url FROM users WHERE id = ANY($1)`,
[Array.from(userIds)] [Array.from(userIds)]
); );
@@ -559,9 +575,8 @@ app.get('/api/messages', authenticate, async (req, res) => {
app.patch('/api/messages/:id/read', authenticate, async (req, res) => { app.patch('/api/messages/:id/read', authenticate, async (req, res) => {
const messageId = req.params.id; const messageId = req.params.id;
const userId = req.user.id; const userId = req.user.id;
const pool = (typeof virtualWorldPool !== 'undefined' && virtualWorldPool) ? virtualWorldPool : db;
try { try {
await pool.query(` await virtualWorldPool.query(`
CREATE TABLE IF NOT EXISTS messages ( CREATE TABLE IF NOT EXISTS messages (
id SERIAL PRIMARY KEY, id SERIAL PRIMARY KEY,
sender_id INT NOT NULL, sender_id INT NOT NULL,
@@ -574,11 +589,11 @@ app.patch('/api/messages/:id/read', authenticate, async (req, res) => {
console.warn('[PATCH /api/messages/:id/read] ensure table failed:', e.message); console.warn('[PATCH /api/messages/:id/read] ensure table failed:', e.message);
} }
try { try {
const checkRes = await pool.query(`SELECT id FROM messages WHERE id = $1 AND receiver_id = $2`, [messageId, userId]); const checkRes = await virtualWorldPool.query(`SELECT id FROM messages WHERE id = $1 AND receiver_id = $2`, [messageId, userId]);
if (checkRes.rows.length === 0) { if (checkRes.rows.length === 0) {
return res.status(404).json({ error: 'Сообщение не найдено или доступ запрещен' }); return res.status(404).json({ error: 'Сообщение не найдено или доступ запрещен' });
} }
await pool.query(`UPDATE messages SET is_read = true WHERE id = $1`, [messageId]); await virtualWorldPool.query(`UPDATE messages SET is_read = true WHERE id = $1`, [messageId]);
res.status(204).end(); res.status(204).end();
} catch (err) { } catch (err) {
console.error('[PATCH /api/messages/:id/read] error:', err); console.error('[PATCH /api/messages/:id/read] error:', err);
@@ -979,10 +994,9 @@ app.post('/api/listen', authenticate, async (req, res) => {
} }
// Выбираем пул: если есть отдельный пул, берём его, иначе общий db // Выбираем пул: если есть отдельный пул, берём его, иначе общий db
const pool = (typeof virtualWorldPool !== 'undefined' && virtualWorldPool) ? virtualWorldPool : db;
try { try {
// Создаём таблицу при необходимости // Создаём таблицу при необходимости
await pool.query(` await virtualWorldPool.query(`
CREATE TABLE IF NOT EXISTS json_listened ( CREATE TABLE IF NOT EXISTS json_listened (
id SERIAL PRIMARY KEY, id SERIAL PRIMARY KEY,
player_id TEXT NOT NULL, player_id TEXT NOT NULL,
@@ -994,7 +1008,7 @@ app.post('/api/listen', authenticate, async (req, res) => {
} }
const q = `INSERT INTO json_listened (player_id, json_filename, listened_at) VALUES ($1, $2, NOW())`; const q = `INSERT INTO json_listened (player_id, json_filename, listened_at) VALUES ($1, $2, NOW())`;
await pool.query(q, [String(effectivePlayerId), String(json_filename)]); await virtualWorldPool.query(q, [String(effectivePlayerId), String(json_filename)]);
console.log('[API /api/listen] Saved listened:', { player: effectivePlayerId, json: json_filename }); console.log('[API /api/listen] Saved listened:', { player: effectivePlayerId, json: json_filename });
return res.status(200).json({ success: true }); return res.status(200).json({ success: true });
} catch (err) { } catch (err) {
@@ -1179,50 +1193,29 @@ app.get('/api/quests/progress', authenticate, async (req, res) => {
console.log("Загрузка на сервере. ID пользователя:", req.user.id); console.log("Загрузка на сервере. ID пользователя:", req.user.id);
try { try {
// Получаем email пользователя из основной БД (или из токена) // ВМЕСТО получения email, используем напрямую ID пользователя
const fallbackEmail = req.user?.email || req.user?.username || null; const userId = req.user.id.toString(); // Преобразуем в строку для consistency
const userRes = await db.query('SELECT email FROM users WHERE id = $1', [req.user.id]);
if (userRes.rows.length === 0) {
if (!fallbackEmail) {
return res.status(404).json({ error: 'Пользователь не найден' });
}
}
const userEmail = userRes.rows[0]?.email || fallbackEmail;
// Получаем список всех квестов с их JSON файлами // Получаем список всех квестов с их JSON файлами
const pool = (typeof virtualWorldPool !== 'undefined' && virtualWorldPool) ? virtualWorldPool : db; const questsQuery = await virtualWorldPool.query(`
const questsQuery = await pool.query(`
SELECT q.id, q.title, qj.json_filename SELECT q.id, q.title, qj.json_filename
FROM quests q FROM quests q
JOIN quest_jsons qj ON q.id = qj.quest_id JOIN quest_jsons qj ON q.id = qj.quest_id
ORDER BY q.id ORDER BY q.id
`); `);
// Получаем JSON файлы, которые прослушал игрок // Получаем JSON файлы, которые прослушал игрок по его ID
// Гарантируем наличие таблицы json_listened const listenedQuery = await virtualWorldPool.query(`
try {
await pool.query(`
CREATE TABLE IF NOT EXISTS json_listened (
id SERIAL PRIMARY KEY,
player_id TEXT NOT NULL,
json_filename TEXT NOT NULL,
listened_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
)`);
} catch (e) {
console.warn('[/api/quests/progress] ensure table json_listened failed:', e.message);
}
const listenedQuery = await pool.query(`
SELECT json_filename FROM json_listened SELECT json_filename FROM json_listened
WHERE player_id = $1 WHERE player_id = $1
`, [userEmail]); `, [userId]); // Используем ID вместо email
console.log("Результат запроса listenedQuery:", listenedQuery.rows); console.log("Результат запроса listenedQuery для ID", userId, ":", listenedQuery.rows);
const listenedFiles = new Set(listenedQuery.rows.map(row => row.json_filename)); const listenedFiles = new Set(listenedQuery.rows.map(row => row.json_filename));
console.log("Прослушанные файлы:", Array.from(listenedFiles)); console.log("Прослушанные файлы:", Array.from(listenedFiles));
// Остальной код остается без изменений... // Остальной код без изменений...
const questsMap = new Map(); const questsMap = new Map();
questsQuery.rows.forEach(row => { questsQuery.rows.forEach(row => {
if (!questsMap.has(row.id)) { if (!questsMap.has(row.id)) {

View File

@@ -1,18 +1,19 @@
/* /*
gjhghjhgjghj
- Проблема с игроками они множатся - Проблема с игроками они множатся
- Проблема с перемещением между городами (исчезновение и появление игроков) - Проблема с перемещением между городами (исчезновение и появление игроков)
- Проблема с Null полусферами - Проблема с Null полусферами
*/ */
import React, { useState, useEffect, useRef } from 'react'; import PF from 'pathfinding';
import React, { useEffect, useRef, useState } from 'react';
import { io } from 'socket.io-client';
import * as THREE from 'three'; import * as THREE from 'three';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js'; import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
import PF from 'pathfinding';
import { io } from 'socket.io-client';
import DoubleTapWrapper from './pages/DoubleTapWrapper';
import OrgControlPanel from './components/OrgControlPanel';
import Inventory from './components/Inventory';
import { useDialogManager } from './components/DialogSystem/DialogManager'; import { useDialogManager } from './components/DialogSystem/DialogManager';
import { DialogWindow } from './components/DialogSystem/DialogWindow'; import { DialogWindow } from './components/DialogSystem/DialogWindow';
import Inventory from './components/Inventory';
import OrgControlPanel from './components/OrgControlPanel';
import DoubleTapWrapper from './pages/DoubleTapWrapper';
import WaveformPlayer from './pages/WaveformPlayer'; import WaveformPlayer from './pages/WaveformPlayer';
function Game({ avatarUrl, gender }) { function Game({ avatarUrl, gender }) {
@@ -768,7 +769,7 @@ function Game({ avatarUrl, gender }) {
// и помечаем как isNpc/npcId для fallback // и помечаем как isNpc/npcId для fallback
objGltf.scene.userData.isNpc = true; objGltf.scene.userData.isNpc = true;
objGltf.scene.userData.npcId = npcId; objGltf.scene.userData.npcId = npcId;
} catch (_) {} } catch (_) { }
} }
} catch (e) { } catch (e) {
console.warn('Не удалось загрузить объект интерьера', o.model_url, e); console.warn('Не удалось загрузить объект интерьера', o.model_url, e);
@@ -799,7 +800,7 @@ function Game({ avatarUrl, gender }) {
} }
intGroup.add(mesh); intGroup.add(mesh);
// Плейсхолдер не рендерим, но используем как коллайдер // Плейсхолдер не рендерим, но используем как коллайдер
try { mesh.visible = false; } catch (_) {} try { mesh.visible = false; } catch (_) { }
// Плейсхолдер без GLTF тоже участвует в коллизиях // Плейсхолдер без GLTF тоже участвует в коллизиях
colliders.push(mesh); colliders.push(mesh);
} }
@@ -814,7 +815,7 @@ function Game({ avatarUrl, gender }) {
hit.userData.interactable = true; hit.userData.interactable = true;
hit.userData.payload = { type: o.type || 'marker', id: o.id || null, label: o.label || 'Интерактив' }; hit.userData.payload = { type: o.type || 'marker', id: o.id || null, label: o.label || 'Интерактив' };
hit.visible = true; // кликабелен hit.visible = true; // кликабелен
try { if (hit.material) hit.material.visible = false; } catch (_) {} try { if (hit.material) hit.material.visible = false; } catch (_) { }
intGroup.add(hit); intGroup.add(hit);
interiorInteractablesRef.current.push(hit); interiorInteractablesRef.current.push(hit);
} }
@@ -1115,7 +1116,7 @@ function Game({ avatarUrl, gender }) {
// Гарантируем выход из интерьера на сервере // Гарантируем выход из интерьера на сервере
socketRef.current?.emit('interiorChange', { interiorId: null }); socketRef.current?.emit('interiorChange', { interiorId: null });
// Включаем мир (закрытие могло скрыть город) // Включаем мир (закрытие могло скрыть город)
try { toggleWorldVisibility(true); } catch (_) {} try { toggleWorldVisibility(true); } catch (_) { }
} else if (savedPositionRef.current) { } else if (savedPositionRef.current) {
console.log('[EXIT] No exit coords, using savedPositionRef'); console.log('[EXIT] No exit coords, using savedPositionRef');
playerRef.current.position.copy(savedPositionRef.current); playerRef.current.position.copy(savedPositionRef.current);
@@ -1161,7 +1162,7 @@ function Game({ avatarUrl, gender }) {
console.log('[EXIT AFTER CLEANUP] Position applied'); console.log('[EXIT AFTER CLEANUP] Position applied');
} }
if (typeof lastPlayerPosition !== 'undefined') { if (typeof lastPlayerPosition !== 'undefined') {
try { lastPlayerPosition = playerRef.current.position.clone(); } catch (_) {} try { lastPlayerPosition = playerRef.current.position.clone(); } catch (_) { }
} }
socketRef.current?.emit('playerMovement', { socketRef.current?.emit('playerMovement', {
x: playerRef.current.position.x, x: playerRef.current.position.x,
@@ -1273,7 +1274,6 @@ function Game({ avatarUrl, gender }) {
headers: { Authorization: `Bearer ${token}` } headers: { Authorization: `Bearer ${token}` }
}); });
if (res.ok) { if (res.ok) {
console.log("Попытка не удалась");
const data = await res.json(); const data = await res.json();
setQuestsProgress(data); setQuestsProgress(data);
} else { } else {
@@ -1350,7 +1350,7 @@ function Game({ avatarUrl, gender }) {
const data = await res.json(); const data = await res.json();
setTelegramContacts(data); setTelegramContacts(data);
} else { } else {
const txt = await res.text().catch(()=> ''); const txt = await res.text().catch(() => '');
console.error('Ошибка загрузки контактов Telegram', res.status, txt); console.error('Ошибка загрузки контактов Telegram', res.status, txt);
setTgError('Не удалось загрузить контакты'); setTgError('Не удалось загрузить контакты');
} }
@@ -1540,7 +1540,7 @@ function Game({ avatarUrl, gender }) {
} catch (err) { } catch (err) {
console.error(`Ошибка при запросе interior_id для объекта ${objectId}:`, err); console.error(`Ошибка при запросе interior_id для объекта ${objectId}:`, err);
} }
} }
async function openOrganizationMenu(orgId) { async function openOrganizationMenu(orgId) {
@@ -1580,11 +1580,11 @@ function Game({ avatarUrl, gender }) {
} }
async function movePlayerToInterior(interiorId) { async function movePlayerToInterior(interiorId) {
await enterInteriorMode(interiorId); await enterInteriorMode(interiorId);
} }
function switchToFirstPersonCamera() { function switchToFirstPersonCamera() {
console.log('switchToFirstPersonCamera вызвана'); console.log('switchToFirstPersonCamera вызвана');
console.log('isInInteriorRef.current:', isInInteriorRef.current); console.log('isInInteriorRef.current:', isInInteriorRef.current);
@@ -1632,9 +1632,9 @@ function switchToFirstPersonCamera() {
); );
console.log('Камера настроена для интерьера'); console.log('Камера настроена для интерьера');
} }
} }
function switchToThirdPersonCamera() { function switchToThirdPersonCamera() {
console.log('switchToThirdPersonCamera вызвана'); console.log('switchToThirdPersonCamera вызвана');
if (orthoCamRef.current) { if (orthoCamRef.current) {
cameraRef.current = orthoCamRef.current; cameraRef.current = orthoCamRef.current;
@@ -1650,21 +1650,21 @@ function switchToThirdPersonCamera() {
console.log('Игрок показан'); console.log('Игрок показан');
} }
fpPitchRef.current = 0; fpPitchRef.current = 0;
} }
function startMove(dir) { function startMove(dir) {
moveInputRef.current[dir] = true; moveInputRef.current[dir] = true;
} }
function stopMove(dir) { function stopMove(dir) {
moveInputRef.current[dir] = false; moveInputRef.current[dir] = false;
} }
// ───────────────────────────────────────────────────── // ─────────────────────────────────────────────────────
// КЛИКИ ВНУТРИ ИНТЕРЬЕРА (интерактивные маркеры/NPC) // КЛИКИ ВНУТРИ ИНТЕРЬЕРА (интерактивные маркеры/NPC)
// ───────────────────────────────────────────────────── // ─────────────────────────────────────────────────────
useEffect(() => { useEffect(() => {
const onClick = (e) => { const onClick = (e) => {
console.log('[INTERIOR CLICK] handler start; isInInterior:', isInInteriorRef.current); console.log('[INTERIOR CLICK] handler start; isInInterior:', isInInteriorRef.current);
if (!isInInteriorRef.current) return; if (!isInInteriorRef.current) return;
@@ -1719,7 +1719,7 @@ useEffect(() => {
console.log('Нажат маркер:', payload); console.log('Нажат маркер:', payload);
} else if (payload.type === 'npc') { } else if (payload.type === 'npc') {
console.log('Нажат NPC:', payload); console.log('Нажат NPC:', payload);
try { if (payload.id) { loadDialog(payload.id); } } catch (_) {} try { if (payload.id) { loadDialog(payload.id); } } catch (_) { }
} else { } else {
console.log('Интерактив:', payload); console.log('Интерактив:', payload);
} }
@@ -1982,7 +1982,7 @@ useEffect(() => {
: window.location.origin; : window.location.origin;
socketRef.current = io(serverUrl, { socketRef.current = io(serverUrl, {
transports: ['websocket','polling'], transports: ['websocket', 'polling'],
auth: { token }, auth: { token },
timeout: 20000 // Увеличиваем timeout до 20 секунд timeout: 20000 // Увеличиваем timeout до 20 секунд
}); });
@@ -2194,7 +2194,7 @@ useEffect(() => {
sprite.scale.set(0.5, 0.5, 1); sprite.scale.set(0.5, 0.5, 1);
// ↓↓↓ добавь это ↓↓↓ // ↓↓↓ добавь это ↓↓↓
sprite.raycast = () => {}; sprite.raycast = () => { };
sprite.userData.isUiSprite = true; sprite.userData.isUiSprite = true;
return sprite; return sprite;
@@ -2254,7 +2254,7 @@ useEffect(() => {
const conn = voiceConnections.current[peerId]; const conn = voiceConnections.current[peerId];
try { try {
conn.audioSender?.replaceTrack(null); conn.audioSender?.replaceTrack(null);
} catch {} } catch { }
conn.peerConnection.close(); conn.peerConnection.close();
conn.audioElement.remove(); conn.audioElement.remove();
delete voiceConnections.current[peerId]; delete voiceConnections.current[peerId];
@@ -2998,11 +2998,9 @@ useEffect(() => {
const isFemale = gender === 'female'; const isFemale = gender === 'female';
const animGender = isFemale ? 'feminine' : 'masculine'; const animGender = isFemale ? 'feminine' : 'masculine';
const idlePath = `/animations/${animGender}/glb/idle/${ const idlePath = `/animations/${animGender}/glb/idle/${isFemale ? 'F_Standing_Idle_001.glb' : 'M_Standing_Idle_001.glb'
isFemale ? 'F_Standing_Idle_001.glb' : 'M_Standing_Idle_001.glb'
}`; }`;
const walkPath = `/animations/${animGender}/glb/locomotion/${ const walkPath = `/animations/${animGender}/glb/locomotion/${isFemale ? 'F_Walk_002.glb' : 'M_Walk_001.glb'
isFemale ? 'F_Walk_002.glb' : 'M_Walk_001.glb'
}`; }`;
console.log('Загружаем анимации:', { idlePath, walkPath }); console.log('Загружаем анимации:', { idlePath, walkPath });
@@ -3536,7 +3534,7 @@ useEffect(() => {
sprite.scale.set(1, 0.25, 1); // Увеличиваем размер спрайта sprite.scale.set(1, 0.25, 1); // Увеличиваем размер спрайта
// ↓↓↓ добавь это ↓↓↓ // ↓↓↓ добавь это ↓↓↓
sprite.raycast = () => {}; sprite.raycast = () => { };
sprite.userData.isUiSprite = true; sprite.userData.isUiSprite = true;
return sprite; return sprite;
@@ -3813,7 +3811,7 @@ useEffect(() => {
new THREE.Vector3(candidate.x + half, candidate.y + height, candidate.z + half) new THREE.Vector3(candidate.x + half, candidate.y + height, candidate.z + half)
); );
// Обновляем мировые матрицы статических коллайдеров для корректных AABB // Обновляем мировые матрицы статических коллайдеров для корректных AABB
try { interiorGroupRef.current && interiorGroupRef.current.updateMatrixWorld(true); } catch (_) {} try { interiorGroupRef.current && interiorGroupRef.current.updateMatrixWorld(true); } catch (_) { }
// В интерьере учитываем только внутренние коллайдеры, без городских объектов // В интерьере учитываем только внутренние коллайдеры, без городских объектов
const blockingMeshes = Array.isArray(interiorCollidersRef.current) const blockingMeshes = Array.isArray(interiorCollidersRef.current)
@@ -4070,7 +4068,7 @@ useEffect(() => {
zIndex: 10000, zIndex: 10000,
width: 260, width: 260,
}}> }}>
{[{label:'Сытость', value:satiety}, {label:'Жажда', value:thirst}].map((bar) => ( {[{ label: 'Сытость', value: satiety }, { label: 'Жажда', value: thirst }].map((bar) => (
<div key={bar.label} style={{ <div key={bar.label} style={{
background: 'rgba(15,15,20,0.75)', background: 'rgba(15,15,20,0.75)',
borderRadius: 12, borderRadius: 12,
@@ -4079,8 +4077,8 @@ useEffect(() => {
backdropFilter: 'blur(4px)', backdropFilter: 'blur(4px)',
}}> }}>
<div style={{ <div style={{
display:'flex', justifyContent:'space-between', display: 'flex', justifyContent: 'space-between',
fontSize: 13, color:'#B8C0CC', marginBottom: 6, fontSize: 13, color: '#B8C0CC', marginBottom: 6,
fontWeight: 600, letterSpacing: 0.3, fontWeight: 600, letterSpacing: 0.3,
}}> }}>
<span>{bar.label}</span> <span>{bar.label}</span>
@@ -4100,7 +4098,7 @@ useEffect(() => {
background: 'linear-gradient(90deg, #22c55e, #eab308, #ef4444)', background: 'linear-gradient(90deg, #22c55e, #eab308, #ef4444)',
transition: 'width 300ms ease', transition: 'width 300ms ease',
boxShadow: '0 0 6px rgba(255,255,255,0.35) inset', boxShadow: '0 0 6px rgba(255,255,255,0.35) inset',
}}/> }} />
</div> </div>
</div> </div>
))} ))}
@@ -4523,21 +4521,21 @@ useEffect(() => {
maxWidth: 420, maxWidth: 420,
zIndex: 3000 zIndex: 3000
}}> }}>
<h3 style={{marginTop: 0, marginBottom: 10}}>{orgMenu.name}</h3> <h3 style={{ marginTop: 0, marginBottom: 10 }}>{orgMenu.name}</h3>
{/* orgMenu.menu теперь массив элементов */} {/* orgMenu.menu теперь массив элементов */}
{(!orgMenu.menu || orgMenu.menu.length === 0) && <p>Меню пусто</p>} {(!orgMenu.menu || orgMenu.menu.length === 0) && <p>Меню пусто</p>}
{Array.isArray(orgMenu.menu) && orgMenu.menu.map(it => ( {Array.isArray(orgMenu.menu) && orgMenu.menu.map(it => (
<div key={it.key} style={{display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 8}}> <div key={it.key} style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 8 }}>
<div> <div>
<div style={{fontWeight: 600}}>{it.title || it.key}</div> <div style={{ fontWeight: 600 }}>{it.title || it.key}</div>
{it.price != null && <div style={{opacity: .8, fontSize: 12}}>{Number(it.price)} </div>} {it.price != null && <div style={{ opacity: .8, fontSize: 12 }}>{Number(it.price)} </div>}
</div> </div>
<button onClick={() => purchaseItem(orgMenu.id, it.key)}>Купить</button> <button onClick={() => purchaseItem(orgMenu.id, it.key)}>Купить</button>
</div> </div>
))} ))}
<div style={{textAlign: 'right', marginTop: 10}}> <div style={{ textAlign: 'right', marginTop: 10 }}>
<button onClick={() => setOrgMenu(null)}>Закрыть</button> <button onClick={() => setOrgMenu(null)}>Закрыть</button>
</div> </div>
</div> </div>
@@ -5418,7 +5416,7 @@ useEffect(() => {
src: "https://cdn-icons-png.flaticon.com/512/1828/1828817.png", src: "https://cdn-icons-png.flaticon.com/512/1828/1828817.png",
alt: "Phone", alt: "Phone",
app: "Phone" app: "Phone"
}, },
{ src: "https://cdn-icons-png.flaticon.com/512/1828/1828864.png", alt: "Камера" }, { src: "https://cdn-icons-png.flaticon.com/512/1828/1828864.png", alt: "Камера" },
{ src: "https://cdn-icons-png.flaticon.com/512/1828/1828911.png", alt: "Gallery" }, { src: "https://cdn-icons-png.flaticon.com/512/1828/1828911.png", alt: "Gallery" },
{ src: "https://cdn-icons-png.flaticon.com/512/1828/1828970.png", alt: "Music" }, { src: "https://cdn-icons-png.flaticon.com/512/1828/1828970.png", alt: "Music" },
@@ -5612,8 +5610,14 @@ useEffect(() => {
)} )}
</div> </div>
<div style={{ padding: 8, display: 'flex', gap: 8, borderTop: '1px solid #eee', background: '#fff' }}> <div style={{ padding: 8, display: 'flex', gap: 8, borderTop: '1px solid #eee', background: '#fff' }}>
<input type="text" value={newMessage} onChange={(e) => setNewMessage(e.target.value)} placeholder="Сообщение" onKeyDown={(e) => { if (e.key === 'Enter') sendMessage(); }} style={{ flex: 1, padding: '10px 12px', borderRadius: 12, border: '1px solid #ddd' }} /> <input
<button onClick={sendMessage} style={{ padding: '10px 14px', background: '#0084ff', color: '#fff', border: 'none', borderRadius: 12, cursor: 'pointer' }}>Отправить</button> type="text"
value={newMessage}
onChange={(e) => setNewMessage(e.target.value)}
placeholder="Сообщение"
onKeyDown={(e) => { if (e.key === 'Enter') sendMessage(); }}
style={{ flex: 1, width: '80%', padding: '8px 8px', borderRadius: 12, border: '1px solid #ddd' }} />
<button onClick={sendMessage} style={{ padding: '8px 8px', background: '#0084ff', color: '#fff', border: 'none', borderRadius: 12, cursor: 'pointer' }}></button>
</div> </div>
</> </>
)} )}