player manager, console update
This commit is contained in:
parent
57a7accdd7
commit
f01d9d56ad
@ -2,8 +2,9 @@ import React, { useState, useEffect, useRef } from "react";
|
|||||||
import "./Console.css";
|
import "./Console.css";
|
||||||
import { useSocket } from "../../hooks/useSocket";
|
import { useSocket } from "../../hooks/useSocket";
|
||||||
import ConsoleManager from "../../services/ConsoleManager";
|
import ConsoleManager from "../../services/ConsoleManager";
|
||||||
|
import PlayerManager from "../../services/PlayerManager"; // Імпортуємо менеджер гравців
|
||||||
|
|
||||||
const Console = ({ players = [] }) => {
|
const Console = () => {
|
||||||
const { socket } = useSocket();
|
const { socket } = useSocket();
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
const [input, setInput] = useState("");
|
const [input, setInput] = useState("");
|
||||||
@ -11,6 +12,8 @@ const Console = ({ players = [] }) => {
|
|||||||
const [selectedIndex, setSelectedIndex] = useState(0);
|
const [selectedIndex, setSelectedIndex] = useState(0);
|
||||||
const [logs, setLogs] = useState(ConsoleManager.logs);
|
const [logs, setLogs] = useState(ConsoleManager.logs);
|
||||||
|
|
||||||
|
const [playerNames, setPlayerNames] = useState([]);
|
||||||
|
|
||||||
const inputRef = useRef(null);
|
const inputRef = useRef(null);
|
||||||
const scrollRef = useRef(null);
|
const scrollRef = useRef(null);
|
||||||
|
|
||||||
@ -18,6 +21,21 @@ const Console = ({ players = [] }) => {
|
|||||||
ConsoleManager.init(socket, setLogs);
|
ConsoleManager.init(socket, setLogs);
|
||||||
}, [socket]);
|
}, [socket]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const updatePlayers = (data) => {
|
||||||
|
setPlayerNames([...data.online, ...data.offline]);
|
||||||
|
};
|
||||||
|
|
||||||
|
const unsubscribe = PlayerManager.subscribe(updatePlayers);
|
||||||
|
|
||||||
|
setPlayerNames([
|
||||||
|
...PlayerManager.onlinePlayers,
|
||||||
|
...PlayerManager.offlinePlayers,
|
||||||
|
]);
|
||||||
|
|
||||||
|
return () => unsubscribe();
|
||||||
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (scrollRef.current) {
|
if (scrollRef.current) {
|
||||||
scrollRef.current.scrollTop = scrollRef.current.scrollHeight;
|
scrollRef.current.scrollTop = scrollRef.current.scrollHeight;
|
||||||
@ -36,12 +54,11 @@ const Console = ({ players = [] }) => {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setSuggestions(ConsoleManager.getSuggestions(input, players));
|
setSuggestions(ConsoleManager.getSuggestions(input, playerNames));
|
||||||
setSelectedIndex(0);
|
setSelectedIndex(0);
|
||||||
}, [input, players]);
|
}, [input, playerNames]);
|
||||||
|
|
||||||
const handleKeyDown = (e) => {
|
const handleKeyDown = (e) => {
|
||||||
// 1. Tab для автозаповнення
|
|
||||||
if (e.key === "Tab" && suggestions.length > 0) {
|
if (e.key === "Tab" && suggestions.length > 0) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const parts = input.split(" ");
|
const parts = input.split(" ");
|
||||||
@ -74,6 +91,7 @@ const Console = ({ players = [] }) => {
|
|||||||
|
|
||||||
const handleSubmit = (e) => {
|
const handleSubmit = (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
if (!input.trim()) return;
|
||||||
ConsoleManager.execute(input);
|
ConsoleManager.execute(input);
|
||||||
setInput("");
|
setInput("");
|
||||||
};
|
};
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import React, {
|
|||||||
useEffect,
|
useEffect,
|
||||||
} from "react";
|
} from "react";
|
||||||
import { io } from "socket.io-client";
|
import { io } from "socket.io-client";
|
||||||
|
import PlayerManager from "../services/PlayerManager"; // Імпортуємо менеджер
|
||||||
|
|
||||||
export const SocketContext = createContext(null);
|
export const SocketContext = createContext(null);
|
||||||
|
|
||||||
@ -19,12 +20,12 @@ export const SocketProvider = ({ children }) => {
|
|||||||
if (socketRef.current?.connected) {
|
if (socketRef.current?.connected) {
|
||||||
return socketRef.current;
|
return socketRef.current;
|
||||||
}
|
}
|
||||||
|
|
||||||
const userInfo = JSON.parse(localStorage.getItem("user"));
|
const userInfo = JSON.parse(localStorage.getItem("user"));
|
||||||
|
|
||||||
const newSocket = io(url, {
|
const newSocket = io(url, {
|
||||||
auth: { token, username: userInfo.username },
|
auth: { token, username: userInfo?.username },
|
||||||
transports: ["websocket"],
|
transports: ["websocket"],
|
||||||
upgrade: true,
|
|
||||||
withCredentials: true,
|
|
||||||
reconnectionAttempts: 5,
|
reconnectionAttempts: 5,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -33,8 +34,19 @@ export const SocketProvider = ({ children }) => {
|
|||||||
setIsConnected(true);
|
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) => {
|
newSocket.on("disconnect", (reason) => {
|
||||||
console.log("❌ Disconnected:", reason);
|
|
||||||
setIsConnected(false);
|
setIsConnected(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -2,7 +2,6 @@ import GameDataManager from "./GameDataManager";
|
|||||||
|
|
||||||
class ConsoleManager {
|
class ConsoleManager {
|
||||||
constructor() {
|
constructor() {
|
||||||
// Залишили тільки вказані команди
|
|
||||||
this.commands = ["/clear", "/give", "/set_exp"];
|
this.commands = ["/clear", "/give", "/set_exp"];
|
||||||
this.logs = ["[System] Console Manager Ready. Press F9 to hide."];
|
this.logs = ["[System] Console Manager Ready. Press F9 to hide."];
|
||||||
this.history = [];
|
this.history = [];
|
||||||
@ -27,46 +26,45 @@ class ConsoleManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getSuggestions(input, players = []) {
|
getSuggestions(input, players = []) {
|
||||||
|
const safePlayers = (players || []).filter((p) => typeof p === "string");
|
||||||
|
|
||||||
const parts = input.split(" ");
|
const parts = input.split(" ");
|
||||||
if (parts.length === 0) return [];
|
if (parts.length === 0) return [];
|
||||||
|
|
||||||
const cmd = parts[0].toLowerCase();
|
const cmd = parts[0].toLowerCase();
|
||||||
|
|
||||||
// 1. Пропозиції команд
|
|
||||||
if (parts.length === 1 && input.startsWith("/")) {
|
if (parts.length === 1 && input.startsWith("/")) {
|
||||||
return this.commands.filter((c) => c.startsWith(cmd));
|
return this.commands.filter((c) => c.startsWith(cmd));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. Логіка для /give (Гравець -> Предмет)
|
|
||||||
if (cmd === "/give") {
|
if (cmd === "/give") {
|
||||||
if (parts.length === 2) {
|
if (parts.length === 2) {
|
||||||
const search = parts[1].toLowerCase();
|
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) {
|
if (parts.length === 3) {
|
||||||
const search = parts[2].toLowerCase();
|
const search = parts[2].toLowerCase();
|
||||||
const itemIds = Array.from(GameDataManager.items.keys());
|
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 (cmd === "/set_exp") {
|
||||||
if (parts.length === 2) {
|
if (parts.length === 2) {
|
||||||
const search = parts[1].toLowerCase();
|
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) {
|
if (cmd === "/clear" && parts.length === 2) {
|
||||||
const search = parts[1].toLowerCase();
|
const search = parts[1].toLowerCase();
|
||||||
return players.filter((p) => p.toLowerCase().startsWith(search));
|
return safePlayers.filter((p) => p.toLowerCase().startsWith(search));
|
||||||
}
|
}
|
||||||
|
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
execute(input) {
|
execute(input) {
|
||||||
const trimmed = input.trim();
|
const trimmed = input.trim();
|
||||||
if (!trimmed) return;
|
if (!trimmed) return;
|
||||||
|
|||||||
46
client/src/services/PlayerManager.js
Normal file
46
client/src/services/PlayerManager.js
Normal 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();
|
||||||
@ -93,7 +93,7 @@ const GameInterface = ({ onExit }) => {
|
|||||||
{tabs[activeTab] || <DashboardTab />}
|
{tabs[activeTab] || <DashboardTab />}
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<Console players={onlinePlayers} />
|
<Console />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user