adminpage

This commit is contained in:
2025-08-03 23:18:49 +05:30
parent f618a02ce3
commit 511bbb87b4
6 changed files with 279 additions and 9 deletions

View File

@@ -33,11 +33,17 @@ interface ApplyMultiplierBonusMessage extends AuthenticatedMessage { // New mess
multiplierBonus: number;
}
interface AdminBroadcastMessage extends AuthenticatedMessage {
type: 'admin-broadcast';
message: string;
targetUserId?: string; // Optional: if broadcasting to a specific user
}
interface UserJoinMessage extends AuthenticatedMessage {
type: 'user-join';
}
type Message = ClickMessage | PurchaseUpgradeMessage | ApplyMultiplierBonusMessage | UserJoinMessage; // Updated Message type
type Message = ClickMessage | PurchaseUpgradeMessage | ApplyMultiplierBonusMessage | UserJoinMessage | AdminBroadcastMessage; // Updated Message type
const UPGRADES = {
clickMultiplier: { baseCost: 10, multiplier: 1.5, clickBonus: 1 },
@@ -59,6 +65,7 @@ const MILESTONES = [
export default class GameServer implements Party.Server {
clerkClient: ReturnType<typeof createClerkClient>;
private userConnections: Map<string, Party.Connection> = new Map(); // Map userId to connection
constructor(readonly party: Party.Party) {
this.clerkClient = createClerkClient({
@@ -86,6 +93,16 @@ export default class GameServer implements Party.Server {
conn.send(JSON.stringify({ type: 'game-state', state: this.gameState }));
}
onClose(conn: Party.Connection) {
// Remove the connection from the map when it closes
for (const [userId, connection] of this.userConnections.entries()) {
if (connection === conn) {
this.userConnections.delete(userId);
break;
}
}
}
async onMessage(message: string, sender: Party.Connection) {
const data = JSON.parse(message) as Message;
@@ -145,6 +162,8 @@ export default class GameServer implements Party.Server {
if (this.gameState.users[currentUserId].bonusMultiplier === undefined) {
this.gameState.users[currentUserId].bonusMultiplier = 1;
}
// Store the connection for targeted broadcasts
this.userConnections.set(currentUserId, sender);
}
switch (data.type) {
@@ -173,6 +192,13 @@ export default class GameServer implements Party.Server {
console.warn(`Unauthenticated apply-multiplier-bonus from ${currentUserId} ignored.`);
}
break;
case 'admin-broadcast':
if (isAuthenticated && currentUserId === this.party.env.CLERK_ADMIN_USERID) {
this.handleAdminBroadcast(data);
} else {
console.warn(`Unauthorized admin broadcast attempt from ${currentUserId}.`);
}
break;
}
this.broadcast();
@@ -209,6 +235,29 @@ export default class GameServer implements Party.Server {
this.checkMilestones();
}
handleAdminBroadcast(data: AdminBroadcastMessage) {
const broadcastPayload = {
type: 'admin-message',
message: data.message,
sender: 'Admin'
};
if (data.targetUserId) {
// Send to a specific user using the stored connection
const targetConn = this.userConnections.get(data.targetUserId);
if (targetConn) {
targetConn.send(JSON.stringify(broadcastPayload));
console.log(`Admin broadcasted to user ${data.targetUserId}: ${data.message}`);
} else {
console.warn(`Target user ${data.targetUserId} not found or not connected.`);
}
} else {
// Broadcast to all connections
this.party.broadcast(JSON.stringify(broadcastPayload));
console.log(`Admin broadcasted to all users: ${data.message}`);
}
}
handleApplyMultiplierBonus(data: ApplyMultiplierBonusMessage, authenticatedUserId: string) {
if (!this.gameState.users[authenticatedUserId]) {
console.warn(`User ${authenticatedUserId} not found for multiplier bonus application.`);