diff --git a/game-server/src/game/AdminManager.js b/game-server/src/game/AdminManager.js
new file mode 100644
index 0000000..452112d
--- /dev/null
+++ b/game-server/src/game/AdminManager.js
@@ -0,0 +1,91 @@
+const { Player, Inventory } = require("../models");
+const DatapackLoader = require("../game/DatapackLoader");
+const notificationManager = require("./NotificationManager");
+
+class AdminManager {
+ constructor() {
+ this.io = null;
+ }
+
+ init(io) {
+ this.io = io;
+ }
+
+ async giveItem(targetName, itemId, amount) {
+ const targetPlayer = await Player.findOne({
+ where: { username: targetName },
+ });
+ if (!targetPlayer) throw new Error(`Player '${targetName}' not found.`);
+
+ const itemData = DatapackLoader.getItem(itemId);
+ if (!itemData) throw new Error(`Item ID '${itemId}' does not exist.`);
+
+ const [inventoryItem, created] = await Inventory.findOrCreate({
+ where: { playerId: targetPlayer.id, itemId: itemId },
+ defaults: { quantity: amount },
+ });
+
+ if (!created) {
+ await inventoryItem.increment("quantity", { by: amount });
+ }
+
+ await notificationManager.send({
+ playerId: targetPlayer.id,
+ type: "item_received",
+ title: "Items Received",
+ message: `Admin granted you ${amount}x ${itemData.name || itemId}.`,
+ data: { itemId, amount },
+ priority: "normal",
+ });
+
+ this._updatePlayerInventory(targetPlayer.id);
+ return { targetName, itemId, amount };
+ }
+
+ async clearInventory(targetName) {
+ const targetPlayer = await Player.findOne({
+ where: { username: targetName },
+ });
+ if (!targetPlayer) throw new Error(`Player '${targetName}' not found.`);
+
+ await Inventory.destroy({ where: { playerId: targetPlayer.id } });
+
+ await notificationManager.send({
+ playerId: targetPlayer.id,
+ type: "inventory_clear",
+ title: "Inventory Wiped",
+ message: "Your inventory has been cleared by an administrator.",
+ priority: "high",
+ });
+
+ this._updatePlayerInventory(targetPlayer.id, true);
+ return targetName;
+ }
+
+ async reloadData() {
+ DatapackLoader.loadAll();
+ if (this.io) {
+ this.io.emit("admin:log", "System: Datapacks reloaded by admin.");
+ }
+ }
+
+ async _updatePlayerInventory(playerId, isEmpty = false) {
+ if (!this.io) return;
+
+ const targetSocket = [...this.io.sockets.sockets.values()].find(
+ (s) => s.user?.id === playerId,
+ );
+
+ if (targetSocket) {
+ const items = isEmpty
+ ? []
+ : await Inventory.findAll({
+ where: { playerId },
+ attributes: ["itemId", "quantity"],
+ });
+ targetSocket.emit("player:inventory_data", items);
+ }
+ }
+}
+
+module.exports = new AdminManager();
diff --git a/game-server/src/game/NotificationManager.js b/game-server/src/game/NotificationManager.js
index 62e3da4..9a97e09 100644
--- a/game-server/src/game/NotificationManager.js
+++ b/game-server/src/game/NotificationManager.js
@@ -1,33 +1,93 @@
const Notification = require("../models/Notification");
class NotificationManager {
- async createNotification({ playerId, type, title, message, data = {} }) {
+ constructor() {
+ this.io = null;
+ }
+
+ init(io) {
+ this.io = io;
+ console.log("[NotificationManager] Initialized with Socket.io");
+ }
+
+ async send({
+ playerId,
+ type = "info",
+ title,
+ message,
+ data = {},
+ priority = "normal",
+ }) {
try {
- return await Notification.create({
+ const notification = await Notification.create({
playerId,
type,
title,
message,
data,
+ priority,
+ isRead: false,
});
+
+ const targetSocket = this._getSocketByPlayerId(playerId);
+
+ if (targetSocket) {
+ targetSocket.emit("notification:new", notification);
+
+ const unreadCount = await this.getUnreadCount(playerId);
+ targetSocket.emit("notifications:unread_count", unreadCount);
+ }
+
+ return notification;
} catch (error) {
- console.error("Notify Error:", error);
+ console.error(
+ `[NotificationManager] Error sending to ${playerId}:`,
+ error,
+ );
}
}
- async getPlayerNotifications(playerId) {
+ _getSocketByPlayerId(playerId) {
+ if (!this.io) return null;
+ return [...this.io.sockets.sockets.values()].find(
+ (s) => s.user?.id === playerId,
+ );
+ }
+ async getPlayerHistory(playerId, limit = 50) {
return await Notification.findAll({
where: { playerId },
order: [["createdAt", "DESC"]],
- limit: 20,
+ limit,
});
}
- async markAsRead(notificationId) {
- return await Notification.update(
+ async getUnreadCount(playerId) {
+ return await Notification.count({
+ where: { playerId, isRead: false },
+ });
+ }
+
+ async markAsRead(notificationId, playerId) {
+ await Notification.update(
{ isRead: true },
- { where: { id: notificationId } },
+ { where: { id: notificationId, playerId } },
);
+ return await this.getUnreadCount(playerId);
+ }
+
+ async markAllAsRead(playerId) {
+ await Notification.update(
+ { isRead: true },
+ { where: { playerId, isRead: false } },
+ );
+ return 0;
+ }
+
+ async delete(notificationId, playerId) {
+ await Notification.destroy({
+ where: { id: notificationId, playerId },
+ });
+ return await this.getUnreadCount(playerId);
}
}
diff --git a/game-server/src/index.js b/game-server/src/index.js
index 36759da..f311579 100644
--- a/game-server/src/index.js
+++ b/game-server/src/index.js
@@ -10,6 +10,8 @@ const DatapackLoader = require("./game/DatapackLoader.js");
const path = require("path");
const app = express();
const economyService = require("./game/EconomyService.js");
+const NotificationManager = require("./game/NotificationManager.js");
+const AdminManager = require("./game/AdminManager.js");
app.use(
cors({
@@ -80,6 +82,8 @@ server.listen(config.port, async () => {
DatapackLoader.init(datapacksPath, io);
await sequelize.initDatabase();
initSockets(io);
+ NotificationManager.init(io);
+ AdminManager.init(io);
economyService.init(io);
await registerInApi();
setInterval(sendHeartbeat, HEARTBEAT_INTERVAL);
diff --git a/game-server/src/sockets/handlers/adminHandler.js b/game-server/src/sockets/handlers/adminHandler.js
index 716199a..9ab1fbe 100644
--- a/game-server/src/sockets/handlers/adminHandler.js
+++ b/game-server/src/sockets/handlers/adminHandler.js
@@ -1,7 +1,8 @@
-const { Player, Inventory } = require("../../models");
-const DatapackLoader = require("../../game/DatapackLoader");
+const adminManager = require("../../game/AdminManager");
module.exports = (io, socket) => {
+ if (!adminManager.io) adminManager.init(io);
+
const handleAdminCommand = async ({ command }) => {
const args = command.trim().split(/\s+/);
const cmd = args[0].toLowerCase();
@@ -13,76 +14,33 @@ module.exports = (io, socket) => {
const amount = parseInt(amountStr) || 1;
if (!targetName || !itemId) {
- throw new Error("Usage: /give [player_name] [item_id] [amount]");
- }
-
- const targetPlayer = await Player.findOne({
- where: { username: targetName },
- });
- if (!targetPlayer)
- throw new Error(`Player '${targetName}' not found in database.`);
-
- const itemData = DatapackLoader.getItem(itemId);
- if (!itemData)
- throw new Error(`Item ID '${itemId}' does not exist in datapacks.`);
-
- const [inventoryItem, created] = await Inventory.findOrCreate({
- where: { playerId: targetPlayer.id, itemId: itemId },
- defaults: { quantity: amount },
- });
-
- if (!created) {
- await inventoryItem.increment("quantity", { by: amount });
+ throw new Error("Usage: /give [player] [item] [amount]");
}
+ const result = await adminManager.giveItem(
+ targetName,
+ itemId,
+ amount,
+ );
socket.emit(
"admin:log",
- `Successfully gave ${amount}x [${itemId}] to ${targetName}.`,
+ `Successfully gave ${result.amount}x [${result.itemId}] to ${result.targetName}.`,
);
-
- const targetSocket = [...io.sockets.sockets.values()].find(
- (s) => s.user?.id === targetPlayer.id,
- );
-
- if (targetSocket) {
- const updatedItems = await Inventory.findAll({
- where: { playerId: targetPlayer.id },
- attributes: ["itemId", "quantity"],
- });
- targetSocket.emit("player:inventory_data", updatedItems);
- targetSocket.emit(
- "admin:log",
- `Admin gave you ${amount}x ${itemId}`,
- );
- }
break;
}
case "/clear": {
const [_, targetName] = args;
- const targetPlayer = await Player.findOne({
- where: { username: targetName },
- });
- if (!targetPlayer) throw new Error("Player not found.");
+ if (!targetName) throw new Error("Usage: /clear [player]");
- await Inventory.destroy({ where: { playerId: targetPlayer.id } });
-
- socket.emit(
- "admin:log",
- `Inventory for ${targetName} has been wiped.`,
- );
-
- const targetSocket = [...io.sockets.sockets.values()].find(
- (s) => s.user?.id === targetPlayer.id,
- );
- if (targetSocket) targetSocket.emit("player:inventory_data", []);
+ const target = await adminManager.clearInventory(targetName);
+ socket.emit("admin:log", `Inventory for ${target} has been wiped.`);
break;
}
case "/reload_data": {
socket.emit("admin:log", "Reloading all datapacks...");
- DatapackLoader.loadAll();
- io.emit("admin:log", "System: Datapacks reloaded by admin.");
+ await adminManager.reloadData();
break;
}
@@ -90,7 +48,6 @@ module.exports = (io, socket) => {
socket.emit("admin:log", `Unknown admin command: ${cmd}`);
}
} catch (err) {
- console.error("Admin Command Error:", err.message);
socket.emit("admin:log", `Error: ${err.message}`);
}
};
diff --git a/game-server/src/sockets/handlers/chatHandler.js b/game-server/src/sockets/handlers/chatHandler.js
index d9cb627..1cdc2b4 100644
--- a/game-server/src/sockets/handlers/chatHandler.js
+++ b/game-server/src/sockets/handlers/chatHandler.js
@@ -42,6 +42,7 @@ module.exports = (io, socket) => {
if (messageData.type === "global") {
io.emit("chat:new_message", messageData);
} else {
+ console.log(payload.receiverId);
socket
.to(`user_${payload.receiverId}`)
.emit("chat:new_message", messageData);
diff --git a/game-server/src/sockets/handlers/friendHandler.js b/game-server/src/sockets/handlers/friendHandler.js
index 6065315..00d2ab3 100644
--- a/game-server/src/sockets/handlers/friendHandler.js
+++ b/game-server/src/sockets/handlers/friendHandler.js
@@ -51,7 +51,7 @@ module.exports = (io, socket) => {
const exists = await Friend.findOne({
where: { playerId: myId, friendId: friendId },
});
-
+ console.log(myId, friendId);
if (!exists) {
await Friend.bulkCreate([
{ playerId: myId, friendId: friendId },
diff --git a/game-server/src/sockets/handlers/notificationHandler.js b/game-server/src/sockets/handlers/notificationHandler.js
index 0f0e1e0..d1d190f 100644
--- a/game-server/src/sockets/handlers/notificationHandler.js
+++ b/game-server/src/sockets/handlers/notificationHandler.js
@@ -37,11 +37,11 @@ module.exports = (io, socket) => {
socket.on("notification:dismiss", async ({ id }) => {
try {
await Notification.destroy({
- where: { id, playerId: socket.player.id },
+ where: { id, playerId: socket.user.id },
});
const unreadCount = await Notification.count({
- where: { playerId: socket.player.id, isRead: false },
+ where: { playerId: socket.user.id, isRead: false },
});
socket.emit("notifications:unread_count", unreadCount);
} catch (e) {