player manager, console update

This commit is contained in:
MaksSlyzar 2026-03-29 10:28:07 +03:00
parent 57a7accdd7
commit f01d9d56ad
5 changed files with 93 additions and 19 deletions

View File

@ -2,8 +2,9 @@ import React, { useState, useEffect, useRef } from "react";
import "./Console.css";
import { useSocket } from "../../hooks/useSocket";
import ConsoleManager from "../../services/ConsoleManager";
import PlayerManager from "../../services/PlayerManager"; // Імпортуємо менеджер гравців
const Console = ({ players = [] }) => {
const Console = () => {
const { socket } = useSocket();
const [isOpen, setIsOpen] = useState(false);
const [input, setInput] = useState("");
@ -11,6 +12,8 @@ const Console = ({ players = [] }) => {
const [selectedIndex, setSelectedIndex] = useState(0);
const [logs, setLogs] = useState(ConsoleManager.logs);
const [playerNames, setPlayerNames] = useState([]);
const inputRef = useRef(null);
const scrollRef = useRef(null);
@ -18,6 +21,21 @@ const Console = ({ players = [] }) => {
ConsoleManager.init(socket, setLogs);
}, [socket]);
useEffect(() => {
const updatePlayers = (data) => {
setPlayerNames([...data.online, ...data.offline]);
};
const unsubscribe = PlayerManager.subscribe(updatePlayers);
setPlayerNames([
...PlayerManager.onlinePlayers,
...PlayerManager.offlinePlayers,
]);
return () => unsubscribe();
}, []);
useEffect(() => {
if (scrollRef.current) {
scrollRef.current.scrollTop = scrollRef.current.scrollHeight;
@ -36,12 +54,11 @@ const Console = ({ players = [] }) => {
}, []);
useEffect(() => {
setSuggestions(ConsoleManager.getSuggestions(input, players));
setSuggestions(ConsoleManager.getSuggestions(input, playerNames));
setSelectedIndex(0);
}, [input, players]);
}, [input, playerNames]);
const handleKeyDown = (e) => {
// 1. Tab для автозаповнення
if (e.key === "Tab" && suggestions.length > 0) {
e.preventDefault();
const parts = input.split(" ");
@ -74,6 +91,7 @@ const Console = ({ players = [] }) => {
const handleSubmit = (e) => {
e.preventDefault();
if (!input.trim()) return;
ConsoleManager.execute(input);
setInput("");
};

View File

@ -6,6 +6,7 @@ import React, {
useEffect,
} from "react";
import { io } from "socket.io-client";
import PlayerManager from "../services/PlayerManager"; // Імпортуємо менеджер
export const SocketContext = createContext(null);
@ -19,12 +20,12 @@ export const SocketProvider = ({ children }) => {
if (socketRef.current?.connected) {
return socketRef.current;
}
const userInfo = JSON.parse(localStorage.getItem("user"));
const newSocket = io(url, {
auth: { token, username: userInfo.username },
auth: { token, username: userInfo?.username },
transports: ["websocket"],
upgrade: true,
withCredentials: true,
reconnectionAttempts: 5,
});
@ -33,8 +34,19 @@ export const SocketProvider = ({ children }) => {
setIsConnected(true);
});
newSocket.on("session:ready", (data) => {
PlayerManager.setInitialState(data.onlinePlayers, data.offlinePlayers);
});
newSocket.on("player:joined", (data) => {
PlayerManager.handlePlayerJoined(data.username);
});
newSocket.on("player:left", (data) => {
PlayerManager.handlePlayerLeft(data.username);
});
newSocket.on("disconnect", (reason) => {
console.log("❌ Disconnected:", reason);
setIsConnected(false);
});

View File

@ -2,7 +2,6 @@ import GameDataManager from "./GameDataManager";
class ConsoleManager {
constructor() {
// Залишили тільки вказані команди
this.commands = ["/clear", "/give", "/set_exp"];
this.logs = ["[System] Console Manager Ready. Press F9 to hide."];
this.history = [];
@ -27,46 +26,45 @@ class ConsoleManager {
}
getSuggestions(input, players = []) {
const safePlayers = (players || []).filter((p) => typeof p === "string");
const parts = input.split(" ");
if (parts.length === 0) return [];
const cmd = parts[0].toLowerCase();
// 1. Пропозиції команд
if (parts.length === 1 && input.startsWith("/")) {
return this.commands.filter((c) => c.startsWith(cmd));
}
// 2. Логіка для /give (Гравець -> Предмет)
if (cmd === "/give") {
if (parts.length === 2) {
const search = parts[1].toLowerCase();
return players.filter((p) => p.toLowerCase().startsWith(search));
return safePlayers.filter((p) => p.toLowerCase().startsWith(search));
}
if (parts.length === 3) {
const search = parts[2].toLowerCase();
const itemIds = Array.from(GameDataManager.items.keys());
return itemIds.filter((id) => id.includes(search)).slice(0, 15);
return itemIds
.filter((id) => id.toLowerCase().includes(search))
.slice(0, 15);
}
}
// 3. Логіка для /set_exp (Гравець -> Кількість)
if (cmd === "/set_exp") {
if (parts.length === 2) {
const search = parts[1].toLowerCase();
return players.filter((p) => p.toLowerCase().startsWith(search));
return safePlayers.filter((p) => p.toLowerCase().startsWith(search));
}
}
// 4. Логіка для /clear (Гравець)
if (cmd === "/clear" && parts.length === 2) {
const search = parts[1].toLowerCase();
return players.filter((p) => p.toLowerCase().startsWith(search));
return safePlayers.filter((p) => p.toLowerCase().startsWith(search));
}
return [];
}
execute(input) {
const trimmed = input.trim();
if (!trimmed) return;

View File

@ -0,0 +1,46 @@
class PlayerManager {
constructor() {
this.onlinePlayers = [];
this.offlinePlayers = [];
this.listeners = new Set();
}
setInitialState(online, offline) {
this.onlinePlayers = online || [];
this.offlinePlayers = offline || [];
this.notify();
}
handlePlayerJoined(username) {
if (!this.onlinePlayers.includes(username)) {
this.onlinePlayers.push(username);
this.offlinePlayers = this.offlinePlayers.filter((u) => u !== username);
this.notify();
}
}
handlePlayerLeft(username) {
this.onlinePlayers = this.onlinePlayers.filter((u) => u !== username);
if (!this.offlinePlayers.includes(username)) {
this.offlinePlayers.push(username);
}
this.notify();
}
subscribe(listener) {
this.listeners.add(listener);
return () => this.listeners.delete(listener);
}
notify() {
this.listeners.forEach((listener) =>
listener({
online: this.onlinePlayers,
offline: this.offlinePlayers,
}),
);
}
}
export default new PlayerManager();

View File

@ -93,7 +93,7 @@ const GameInterface = ({ onExit }) => {
{tabs[activeTab] || <DashboardTab />}
</main>
<Console players={onlinePlayers} />
<Console />
</div>
);
};