Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions scripts/features.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
features:
games:
name: Games
status: dev
dependencies:
- economy

anticheat:
name: Anti-Cheat
status: prod
Expand Down
2 changes: 2 additions & 0 deletions src/core/__tests__/UI.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,11 @@ const { initialize: initShop } = await import('@features/shop/index.js');
const { initialize: initSocial } = await import('@features/social/index.js');
const { initialize: initTeam } = await import('@features/team/index.js');
const { initialize: initTeleport } = await import('@features/teleport/index.js');
const { initialize: initGames } = await import('@features/games/index.js');

// Initialize with wait for async modules (some config loaders are async)
await initEconomy(false);
await initGames(false);
await initEssentials(false);
initKit();
initModeration();
Expand Down
26 changes: 25 additions & 1 deletion src/core/configurations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import type { auctionHouseConfig } from '@features/auction/auctionHouseConfig.de
import type { dailyRewardsConfig } from '@features/daily/dailyRewardsConfig.default.js';
import type { economyConfig } from '@features/economy/economyConfig.js';
import type { WorldProtectionConfig } from '@features/essentials/worldProtectionConfig.default.js';
import type { GamesConfig } from '@features/games/gamesConfig.default.js';
import type { WordleConfig } from '@features/games/wordle/wordleConfig.default.js';
import type ranksConfig from '@features/ranks/ranksConfig.default.js';
import type { shopConfig } from '@features/shop/shopConfig.js';
import type { config as sidebarConfig } from '@features/sidebar/sidebarConfig.default.js';
Expand All @@ -23,6 +25,8 @@ export type FriendConfig = typeof friendConfig;
export type SidebarConfig = typeof sidebarConfig;
export type AuctionHouseConfig = typeof auctionHouseConfig;
export type DailyRewardsConfig = typeof dailyRewardsConfig;
export type GamesConfigType = GamesConfig;
export type WordleConfigType = WordleConfig;

let shopConfigManager: ConfigManager<ShopConfig>,
ranksConfigManager: ConfigManager<RanksConfig>,
Expand All @@ -33,7 +37,9 @@ let shopConfigManager: ConfigManager<ShopConfig>,
sidebarConfigManager: ConfigManager<SidebarConfig>,
auctionHouseConfigManager: ConfigManager<AuctionHouseConfig>,
dailyRewardsConfigManager: ConfigManager<DailyRewardsConfig>,
worldProtectionConfigManager: ConfigManager<WorldProtectionConfig>;
worldProtectionConfigManager: ConfigManager<WorldProtectionConfig>,
gamesConfigManager: ConfigManager<GamesConfig>,
wordleConfigManager: ConfigManager<WordleConfig>;

export const loadWorldProtectionConfig = async (isMigration: boolean) => {
const { worldProtectionConfig } = await import('@features/essentials/worldProtectionConfig.default.js');
Expand Down Expand Up @@ -125,6 +131,24 @@ export const getDailyRewardsConfig = (): DailyRewardsConfig => dailyRewardsConfi
export const saveDailyRewardsConfig = (config: DailyRewardsConfig) => dailyRewardsConfigManager.set(config);
export const resetDailyRewardsConfig = () => dailyRewardsConfigManager.reset();

export const loadGamesConfig = async (isMigration: boolean) => {
const { gamesConfig } = await import('@features/games/gamesConfig.default.js');
gamesConfigManager = createConfigManager('exe:gamesConfig:current', gamesConfig, 'Games');
gamesConfigManager.load(isMigration);
};
export const getGamesConfig = (): GamesConfig => gamesConfigManager.get();
export const saveGamesConfig = (config: GamesConfig) => gamesConfigManager.set(config);
export const resetGamesConfig = () => gamesConfigManager.reset();

export const loadWordleConfig = async (isMigration: boolean) => {
const { wordleConfig } = await import('@features/games/wordle/wordleConfig.default.js');
wordleConfigManager = createConfigManager('exe:wordleConfig:current', wordleConfig, 'Wordle');
wordleConfigManager.load(isMigration);
};
export const getWordleConfig = (): WordleConfig => wordleConfigManager.get();
export const saveWordleConfig = (config: WordleConfig) => wordleConfigManager.set(config);
export const resetWordleConfig = () => wordleConfigManager.reset();

export type ResetRegistryEntry = {
reset: () => Promise<void>;
message: string;
Expand Down
5 changes: 3 additions & 2 deletions src/core/featureRegistry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ export interface FeatureModule {
}

export const featureRegistry = [
{ id: 'anticheat', load: () => import('@features/anticheat/index.js') as Promise<FeatureModule>, subfeatures: undefined },
{ id: 'economy', load: () => import('@features/economy/index.js') as Promise<FeatureModule>, subfeatures: undefined },
{ id: 'games', load: () => import('@features/games/index.js') as Promise<FeatureModule>, subfeatures: undefined },
{ id: 'anticheat', load: () => import('@features/anticheat/index.js') as Promise<FeatureModule>, subfeatures: undefined },
{ id: 'auction', load: () => import('@features/auction/index.js') as Promise<FeatureModule>, subfeatures: undefined },
{ id: 'daily', load: () => import('@features/daily/index.js') as Promise<FeatureModule>, subfeatures: undefined },
{ id: 'essentials', load: () => import('@features/essentials/index.js') as Promise<FeatureModule>, subfeatures: undefined },
Expand All @@ -18,5 +19,5 @@ export const featureRegistry = [
{ id: 'team', load: () => import('@features/team/index.js') as Promise<FeatureModule>, subfeatures: undefined },
{ id: 'teleport', load: () => import('@features/teleport/index.js') as Promise<FeatureModule>, subfeatures: undefined },
{ id: 'vote', load: () => import('@features/vote/index.js') as Promise<FeatureModule>, subfeatures: undefined },
{ id: 'ranks', load: () => import('@features/ranks/index.js') as Promise<FeatureModule>, subfeatures: undefined }
{ id: 'ranks', load: () => import('@features/ranks/index.js') as Promise<FeatureModule>, subfeatures: undefined },
];
54 changes: 54 additions & 0 deletions src/core/ui/configPanelRegistry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -955,5 +955,59 @@ export const configPanelSchema: ConfigCategory[] = [
description: 'Detects speed and fly hacks (experimental).'
}
]
},
{
id: 'games',
title: '§l§aGames Settings§r',
icon: 'textures/ui/controller_icon',
configSource: 'games',
category: 'Games',
settings: [
{
key: 'enabled',
label: 'Enable Games System',
type: 'toggle',
description: 'Master switch for all games.'
}
]
},
{
id: 'wordle',
title: '§l§2Wordle Settings§r',
icon: 'textures/ui/icon_recipe_item',
configSource: 'wordle',
category: 'Games',
settings: [
{
key: 'enabled',
label: 'Enable Wordle',
type: 'toggle',
description: 'Enable or disable the Wordle game.'
},
{
key: 'singlePlayer.enabled',
label: 'Enable Single Player',
type: 'toggle',
description: 'Enable or disable single player Wordle.'
},
{
key: 'multiplayer.enabled',
label: 'Enable Multiplayer',
type: 'toggle',
description: 'Enable or disable multiplayer Wordle.'
},
{
key: 'staffHosted.enabled',
label: 'Enable Staff Hosted Game',
type: 'toggle',
description: 'Enable or disable staff hosted Wordle games.'
},
{
key: 'staffHosted.taxRatePercentage',
label: 'Staff Hosted Tax %',
type: 'textField',
description: 'Tax percentage for staff hosted prize pools.'
}
]
}
];
72 changes: 72 additions & 0 deletions src/core/ui/panelRegistry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,15 @@ export const panelDefinitions: Record<string, PanelDefinition> = {
title: 'Main Menu',
parentPanelId: undefined,
items: [
{
id: 'games',
text: '§l§aGames',
icon: 'textures/ui/controller_icon',
permission: 'ui.panel.member',
actionType: 'openPanel',
actionValue: 'gamesMainPanel',
sortId: 5
},
{
id: 'economy',
text: '§l§6Economy',
Expand Down Expand Up @@ -903,6 +912,69 @@ export const panelDefinitions: Record<string, PanelDefinition> = {
title: 'Placeholder List',
parentPanelId: undefined, // Dynamic
items: [] // Body text
},
gamesMainPanel: {
title: 'Games',
parentPanelId: 'mainPanel',
items: [
{
id: 'wordle',
text: '§l§2Wordle',
icon: 'textures/ui/icon_recipe_item',
permission: 'ui.panel.member',
actionType: 'openPanel',
actionValue: 'wordleMainPanel',
sortId: 10
}
]
},
wordleMainPanel: {
title: 'Wordle Menu',
parentPanelId: 'gamesMainPanel',
items: [
{
id: 'singlePlayer',
text: '§l§aSingle Player',
icon: 'textures/ui/icon_steve',
permission: 'ui.panel.member',
actionType: 'openPanel',
actionValue: 'wordleSinglePlayerPanel',
sortId: 10
},
{
id: 'multiplayer',
text: '§l§eMultiplayer',
icon: 'textures/ui/icon_multiplayer',
permission: 'ui.panel.member',
actionType: 'openPanel',
actionValue: 'wordleMultiplayerPanel',
sortId: 20
},
{
id: 'staffGame',
text: '§l§cStaff Hosted Game',
icon: 'textures/ui/op',
permission: 'ui.panel.mod',
actionType: 'openPanel',
actionValue: 'wordleStaffGamePanel',
sortId: 30
}
]
},
wordleSinglePlayerPanel: {
title: 'Single Player Wordle',
parentPanelId: 'wordleMainPanel',
items: [] // Handled by custom builder
},
wordleMultiplayerPanel: {
title: 'Multiplayer Wordle',
parentPanelId: 'wordleMainPanel',
items: [] // Handled by custom builder
},
wordleStaffGamePanel: {
title: 'Staff Hosted Wordle',
parentPanelId: 'wordleMainPanel',
items: [] // Handled by custom builder
}
};

Expand Down
16 changes: 16 additions & 0 deletions src/core/ui/systemRegistry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,22 @@ export function getSystemRegistry(): SystemDefinition[] {
configPanelId: 'worldProtectionListPanel',
category: 'World',
isSimpleConfig: false
},
{
id: 'games',
title: '§l§aGames System§r',
icon: 'textures/ui/controller_icon',
configPanelId: 'config_games',
category: 'Games',
isSimpleConfig: true
},
{
id: 'wordle',
title: '§l§2Wordle Config§r',
icon: 'textures/ui/icon_recipe_item',
configPanelId: 'config_wordle',
category: 'Games',
isSimpleConfig: true
}
];

Expand Down
5 changes: 5 additions & 0 deletions src/features/games/gamesConfig.default.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export const gamesConfig = {
enabled: true
};

export type GamesConfig = typeof gamesConfig;
30 changes: 30 additions & 0 deletions src/features/games/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { panelRouter } from '@ui/PanelRouter.js';
import { GamesPanelHandler } from './ui/gamesPanel.js';
import { WordlePanelHandler } from './wordle/ui/wordlePanel.js';

export async function initialize(isMigration: boolean) {
panelRouter.register(new GamesPanelHandler());
panelRouter.register(new WordlePanelHandler());

// Register configurations
const { loadGamesConfig, resetGamesConfig, registerConfigReset } = await import('@core/configurations.js');
await loadGamesConfig(isMigration);
registerConfigReset('games', {
reset: resetGamesConfig,
message: 'The games configuration section has been reset to default.'
});

const { loadWordleConfig, resetWordleConfig } = await import('@core/configurations.js');
await loadWordleConfig(isMigration);
registerConfigReset('wordle', {
reset: resetWordleConfig,
message: 'The wordle configuration section has been reset to default.'
});

// Commands will be registered via the standard `@commands/CommandRegistry.js` mechanism if they are standard commands, or inside here.
const { registerWordleCommands } = await import('./wordle/commands/wordle.js');
registerWordleCommands();

const { registerGuessCommand } = await import('./wordle/commands/guess.js');
registerGuessCommand();
}
43 changes: 43 additions & 0 deletions src/features/games/ui/gamesPanel.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { showPanel } from '@core/uiManager.js';
import { isDefined } from '@lib/guards.js';
import * as mc from '@minecraft/server';
import { ActionFormResponse } from '@minecraft/server-ui';
import { getStaticMenuItems } from '@ui/panelBuilder.js';
import { panelDefinitions, PanelItem, UIContext } from '@ui/panelRegistry.js';
import { IPanelHandler } from '@ui/types.js';

export class GamesPanelHandler implements IPanelHandler {
canHandle(panelId: string): boolean {
return panelId === 'gamesMainPanel' || panelId === 'wordleMainPanel';
}

getItems(player: mc.Player, panelId: string, _context: UIContext): Promise<PanelItem[]> {
const items: PanelItem[] = [];
const def = panelDefinitions[panelId];
if (isDefined(def)) {
const staticItems = getStaticMenuItems(player, def);
items.push(...staticItems);
}
return Promise.resolve(items);
}

async handleResponse(player: mc.Player, panelId: string, response: ActionFormResponse, context: UIContext): Promise<void> {
if (response.canceled || response.selection === undefined) return;

const items = await this.getItems(player, panelId, context);
if (response.selection >= 0 && response.selection < items.length) {
const item = items[response.selection];
if (!isDefined(item)) return;
if (item.actionType === 'openPanel') {
return showPanel(player, item.actionValue, { ...context, page: 1 });
}
const { uiActionFunctions } = await import('@core/ui/actionRegistry.js');
const action = uiActionFunctions[item.actionValue];
if (isDefined(action)) {
await action(player, context, panelId);
return;
}
player.sendMessage(`§cAction ${item.actionValue} not mapped.`);
}
}
}
Loading
Loading