@omnitronix/bonnys-fortune-game-engine 1.3.1 → 1.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -15
- package/dist/__tests__/bonnys-fortune-v1.game-engine.test.d.ts +1 -0
- package/dist/bonnys-fortune-v1.game-engine.d.ts +36 -0
- package/dist/bonnys-fortune-v1.game-engine.js +23 -12
- package/dist/bonnys-fortune-v1.game-engine.js.map +1 -1
- package/dist/config/game-logic-config/file-system.game-logic-config-loader.d.ts +4 -0
- package/dist/config/game-logic-config/game-logic-config-loader.d.ts +3 -0
- package/dist/config/game-logic-config/game-logic-config.d.ts +2 -0
- package/dist/config/game-logic-config/game-logic-config.js +2 -0
- package/dist/config/game-logic-config/game-logic-config.js.map +1 -1
- package/dist/config/reel-strips-config/file-system.reel-strips-config-loader.d.ts +4 -0
- package/dist/config/reel-strips-config/reel-strip-option.dto.d.ts +4 -0
- package/dist/config/reel-strips-config/reel-strips-config-loader.d.ts +3 -0
- package/dist/config/reel-strips-config/reel-strips-config.dto.d.ts +5 -0
- package/dist/config/reel-strips-config/reel.dto.d.ts +5 -0
- package/dist/domain/game-round.types.d.ts +9 -0
- package/dist/domain/mappers/reel-strips-config.mapper.d.ts +4 -0
- package/dist/domain/reel-strip-option.d.ts +7 -0
- package/dist/domain/reel-strips-config.d.ts +9 -0
- package/dist/domain/reel.d.ts +8 -0
- package/dist/domain/types/cheat-trigger-bonus-command.d.ts +5 -0
- package/dist/domain/types/{debug-trigger-bonus-command.js → cheat-trigger-bonus-command.js} +1 -1
- package/dist/domain/types/cheat-trigger-bonus-command.js.map +1 -0
- package/dist/domain/types/cheat-trigger-bonus-request.dto.d.ts +5 -0
- package/dist/domain/types/{debug-trigger-bonus-request.dto.js → cheat-trigger-bonus-request.dto.js} +1 -1
- package/dist/domain/types/cheat-trigger-bonus-request.dto.js.map +1 -0
- package/dist/domain/types/cheat-update-bonus-meter-progress-command.d.ts +5 -0
- package/dist/domain/types/{debug-update-bonus-meter-progress-command.js → cheat-update-bonus-meter-progress-command.js} +1 -1
- package/dist/domain/types/cheat-update-bonus-meter-progress-command.js.map +1 -0
- package/dist/domain/types/cheat-update-bonus-meter-progress-request.dto.d.ts +5 -0
- package/dist/domain/types/{debug-update-bonus-meter-progress-request.dto.js → cheat-update-bonus-meter-progress-request.dto.js} +1 -1
- package/dist/domain/types/cheat-update-bonus-meter-progress-request.dto.js.map +1 -0
- package/dist/domain/types/game-spin-command.d.ts +8 -0
- package/dist/domain/types/game-spin-input.dto.d.ts +8 -0
- package/dist/domain/types/game-spin-output.dto.d.ts +119 -0
- package/dist/domain/types/game-symbols.response.dto.d.ts +10 -0
- package/dist/domain/types/reel-strip-option.dto.d.ts +4 -0
- package/dist/domain/types/reel-strips-config.dto.d.ts +5 -0
- package/dist/domain/types/reel.dto.d.ts +5 -0
- package/dist/domain/types/start-bonus-round-command.d.ts +8 -0
- package/dist/domain/types/start-bonus-round-input.dto.d.ts +10 -0
- package/dist/game-engine.interface.d.ts +41 -0
- package/dist/helpers/generate-hash.d.ts +1 -0
- package/dist/helpers/number-helper.d.ts +23 -0
- package/dist/helpers/optional-boolean-mapper.d.ts +1 -0
- package/dist/helpers/uuid-helper.d.ts +3 -0
- package/dist/helpers/validation-helper.d.ts +14 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js.map +1 -1
- package/dist/logger/logger.d.ts +22 -0
- package/dist/logger/logger.js +4 -5
- package/dist/logger/logger.js.map +1 -1
- package/dist/logic/bonnys-fortune.game-logic.d.ts +32 -0
- package/dist/logic/bonnys-fortune.game-logic.js +15 -11
- package/dist/logic/bonnys-fortune.game-logic.js.map +1 -1
- package/dist/logic/bonnys-fortune.spin-generator.d.ts +19 -0
- package/dist/logic/bonnys-fortune.types.d.ts +170 -0
- package/dist/logic/bonnys-fortune.win-calculator.d.ts +10 -0
- package/dist/logic/game-logic-config.interface.d.ts +180 -0
- package/dist/logic/handlers/base-game.handler.d.ts +15 -0
- package/dist/logic/handlers/base-game.handler.js +5 -1
- package/dist/logic/handlers/base-game.handler.js.map +1 -1
- package/dist/logic/handlers/collect-feature-bonus.handler.d.ts +13 -0
- package/dist/logic/handlers/free-spins-bonus.handler.d.ts +11 -0
- package/dist/logic/handlers/steering-to-the-fortune-bonus.handler.d.ts +11 -0
- package/dist/logic/handlers/treasure-hunt-bonus.handler.d.ts +15 -0
- package/dist/rng/DummyRngClient.d.ts +18 -0
- package/dist/rng/DummyRngClient.js +1 -8
- package/dist/rng/DummyRngClient.js.map +1 -1
- package/dist/rng/rng-client.factory.d.ts +7 -0
- package/dist/rng/rng-client.factory.js.map +1 -1
- package/dist/rng/rng-client.interface.d.ts +16 -0
- package/dist/rng/rng-service.d.ts +26 -0
- package/dist/validation/abstract-game-logic-config.validator.d.ts +4 -0
- package/dist/validation/bonnys-fortune/bonnys-fortune-config.dto.d.ts +131 -0
- package/dist/validation/bonnys-fortune/bonnys-fortune-config.dto.js +9 -0
- package/dist/validation/bonnys-fortune/bonnys-fortune-config.dto.js.map +1 -1
- package/dist/validation/bonnys-fortune/bonnys-fortune-config.validator.d.ts +5 -0
- package/dist/validation/custom-decorators/IsNestedIntArray.d.ts +2 -0
- package/dist/validation/game-logic-config-validation.service.d.ts +8 -0
- package/dist/validation/reel-strips-config-validation.service.d.ts +5 -0
- package/package.json +4 -3
- package/src/__tests__/bonnys-fortune-v1.game-engine.test.ts +80 -0
- package/src/bonnys-fortune-v1.game-engine.ts +314 -0
- package/src/config/game-logic-config/file-system.game-logic-config-loader.ts +27 -0
- package/src/config/game-logic-config/game-logic-config-loader.ts +3 -0
- package/src/config/game-logic-config/game-logic-config.ts +382 -0
- package/src/config/reel-strips-config/file-system.reel-strips-config-loader.ts +43 -0
- package/src/config/reel-strips-config/reel-strip-option.dto.ts +13 -0
- package/src/config/reel-strips-config/reel-strips-config-loader.ts +9 -0
- package/src/config/reel-strips-config/reel-strips-config.dto.ts +16 -0
- package/src/config/reel-strips-config/reel.dto.ts +16 -0
- package/src/config/reel-strips-config/reels-BASE.csv +52 -0
- package/src/config/reel-strips-config/reels-BONUS.csv +52 -0
- package/src/domain/game-round.types.ts +10 -0
- package/src/domain/mappers/reel-strips-config.mapper.ts +16 -0
- package/src/domain/reel-strip-option.ts +15 -0
- package/src/domain/reel-strips-config.ts +21 -0
- package/src/domain/reel.ts +17 -0
- package/src/domain/types/cheat-trigger-bonus-command.ts +5 -0
- package/src/domain/types/cheat-trigger-bonus-request.dto.ts +5 -0
- package/src/domain/types/cheat-update-bonus-meter-progress-command.ts +6 -0
- package/src/domain/types/cheat-update-bonus-meter-progress-request.dto.ts +5 -0
- package/src/domain/types/game-spin-command.ts +8 -0
- package/src/domain/types/game-spin-input.dto.ts +8 -0
- package/src/domain/types/game-spin-output.dto.ts +142 -0
- package/src/domain/types/game-symbols.response.dto.ts +11 -0
- package/src/domain/types/reel-strip-option.dto.ts +13 -0
- package/src/domain/types/reel-strips-config.dto.ts +15 -0
- package/src/domain/types/reel.dto.ts +15 -0
- package/src/domain/types/start-bonus-round-command.ts +8 -0
- package/src/domain/types/start-bonus-round-input.dto.ts +10 -0
- package/src/game-engine.interface.ts +59 -0
- package/src/helpers/generate-hash.ts +5 -0
- package/src/helpers/number-helper.ts +41 -0
- package/src/helpers/optional-boolean-mapper.ts +5 -0
- package/src/helpers/uuid-helper.ts +7 -0
- package/src/helpers/validation-helper.ts +27 -0
- package/src/index.ts +3 -0
- package/src/logger/logger.ts +178 -0
- package/src/logic/bonnys-fortune.game-logic.ts +490 -0
- package/src/logic/bonnys-fortune.spin-generator.ts +277 -0
- package/src/logic/bonnys-fortune.types.ts +223 -0
- package/src/logic/bonnys-fortune.win-calculator.ts +210 -0
- package/src/logic/game-logic-config.interface.ts +176 -0
- package/src/logic/handlers/base-game.handler.ts +221 -0
- package/src/logic/handlers/collect-feature-bonus.handler.ts +301 -0
- package/src/logic/handlers/free-spins-bonus.handler.ts +119 -0
- package/src/logic/handlers/steering-to-the-fortune-bonus.handler.ts +118 -0
- package/src/logic/handlers/treasure-hunt-bonus.handler.ts +232 -0
- package/src/rng/DummyRngClient.ts +108 -0
- package/src/rng/rng-client.factory.ts +27 -0
- package/src/rng/rng-client.interface.ts +38 -0
- package/src/rng/rng-service.ts +130 -0
- package/src/validation/abstract-game-logic-config.validator.ts +20 -0
- package/src/validation/bonnys-fortune/bonnys-fortune-config.dto.ts +379 -0
- package/src/validation/bonnys-fortune/bonnys-fortune-config.validator.ts +8 -0
- package/src/validation/custom-decorators/IsNestedIntArray.ts +29 -0
- package/src/validation/game-logic-config-validation.service.ts +28 -0
- package/src/validation/reel-strips-config-validation.service.ts +29 -0
- package/dist/__tests__/comprehensive.test.js +0 -741
- package/dist/__tests__/comprehensive.test.js.map +0 -1
- package/dist/domain/types/debug-trigger-bonus-command.js.map +0 -1
- package/dist/domain/types/debug-trigger-bonus-request.dto.js.map +0 -1
- package/dist/domain/types/debug-update-bonus-meter-progress-command.js.map +0 -1
- package/dist/domain/types/debug-update-bonus-meter-progress-request.dto.js.map +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@omnitronix/bonnys-fortune-game-engine",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.2",
|
|
4
4
|
"description": "Bonnys Fortune Game Engine",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"author": "Omnitronix",
|
|
20
20
|
"license": "UNLICENSED",
|
|
21
21
|
"dependencies": {
|
|
22
|
-
"@omnitronix/rng-client-core": "^1.
|
|
22
|
+
"@omnitronix/rng-client-core": "^1.1.1",
|
|
23
23
|
"class-transformer": "^0.5.1",
|
|
24
24
|
"class-validator": "^0.14.1",
|
|
25
25
|
"reflect-metadata": "^0.1.13",
|
|
@@ -42,7 +42,8 @@
|
|
|
42
42
|
"typescript": "^5.0.0"
|
|
43
43
|
},
|
|
44
44
|
"files": [
|
|
45
|
-
"dist
|
|
45
|
+
"dist",
|
|
46
|
+
"src",
|
|
46
47
|
"README.md"
|
|
47
48
|
],
|
|
48
49
|
"publishConfig": {
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { BonnysFortuneV1GameEngine } from '../bonnys-fortune-v1.game-engine';
|
|
2
|
+
import { GameSpinCommand } from '../domain/types/game-spin-command';
|
|
3
|
+
import { SpinType } from '../domain/types/game-spin-output.dto';
|
|
4
|
+
|
|
5
|
+
describe('BonnysFortuneV1GameEngine', () => {
|
|
6
|
+
let gameEngine: BonnysFortuneV1GameEngine;
|
|
7
|
+
|
|
8
|
+
beforeEach(() => {
|
|
9
|
+
gameEngine = new BonnysFortuneV1GameEngine();
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
it('should initialize the game engine and return info', async () => {
|
|
13
|
+
const info = gameEngine.getGameEngineInfo();
|
|
14
|
+
expect(info.gameCode).toBe('bonnys-fortune');
|
|
15
|
+
expect(info.version).toBe('1.0.0');
|
|
16
|
+
expect(info.rtp).toBe(96);
|
|
17
|
+
expect(info.gameType).toBe('slot');
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it('should process a spin command successfully', async () => {
|
|
21
|
+
const initialPublicState = {
|
|
22
|
+
currentBetAmount: 1,
|
|
23
|
+
bonusMeters: {},
|
|
24
|
+
};
|
|
25
|
+
const initialPrivateState = {
|
|
26
|
+
nextSpinType: SpinType.BASE_GAME_SPIN,
|
|
27
|
+
pendingBonuses: [],
|
|
28
|
+
bonusMetersByAmount: {
|
|
29
|
+
1: {
|
|
30
|
+
bonusGame1: {
|
|
31
|
+
symbolName: 'H1',
|
|
32
|
+
symbolId: 0,
|
|
33
|
+
threshold: 25,
|
|
34
|
+
progress: 0,
|
|
35
|
+
level: 1,
|
|
36
|
+
},
|
|
37
|
+
bonusGame2: {
|
|
38
|
+
symbolName: 'H2',
|
|
39
|
+
symbolId: 1,
|
|
40
|
+
threshold: 25,
|
|
41
|
+
progress: 0,
|
|
42
|
+
level: 1,
|
|
43
|
+
},
|
|
44
|
+
bonusGame3: {
|
|
45
|
+
symbolName: 'H3',
|
|
46
|
+
symbolId: 2,
|
|
47
|
+
threshold: 25,
|
|
48
|
+
progress: 0,
|
|
49
|
+
level: 1,
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const spinCommand = {
|
|
56
|
+
id: 'test-command-123',
|
|
57
|
+
type: 'SPIN',
|
|
58
|
+
payload: {
|
|
59
|
+
sessionId: 'test-session-123',
|
|
60
|
+
betAmount: 1,
|
|
61
|
+
gameCode: 'bonnys-fortune',
|
|
62
|
+
gameVersion: '1.0.0',
|
|
63
|
+
lines: 10,
|
|
64
|
+
creditsPerLine: 1,
|
|
65
|
+
} as GameSpinCommand,
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
const result = await gameEngine.processCommand(
|
|
69
|
+
initialPublicState,
|
|
70
|
+
initialPrivateState,
|
|
71
|
+
spinCommand,
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
expect(result).toBeDefined();
|
|
75
|
+
expect(result.success).toBe(true);
|
|
76
|
+
expect(result.outcome).toBeDefined();
|
|
77
|
+
expect(result.publicState).toBeDefined();
|
|
78
|
+
expect(result.privateState).toBeDefined();
|
|
79
|
+
});
|
|
80
|
+
});
|
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
import {
|
|
2
|
+
BonnysFortunePrivateState,
|
|
3
|
+
BonnysFortunePublicState,
|
|
4
|
+
} from './logic/bonnys-fortune.types';
|
|
5
|
+
import {
|
|
6
|
+
CommandProcessingResult,
|
|
7
|
+
GameActionCommand,
|
|
8
|
+
GameEngine,
|
|
9
|
+
GameEngineConfig,
|
|
10
|
+
GameEngineInfo,
|
|
11
|
+
} from './game-engine.interface';
|
|
12
|
+
|
|
13
|
+
import { BonnysFortuneGameLogic } from './logic/bonnys-fortune.game-logic';
|
|
14
|
+
import { BonnysFortuneGameLogicConfigInterface } from './logic/game-logic-config.interface';
|
|
15
|
+
import { BonnysFortuneGameLogicConfigValidator } from './validation/bonnys-fortune/bonnys-fortune-config.validator';
|
|
16
|
+
import { CheatTriggerBonusCommand } from './domain/types/cheat-trigger-bonus-command';
|
|
17
|
+
import { CheatUpdateBonusMeterProgressCommand } from './domain/types/cheat-update-bonus-meter-progress-command';
|
|
18
|
+
import { FileSystemGameLogicConfigLoader } from './config/game-logic-config/file-system.game-logic-config-loader';
|
|
19
|
+
import { FileSystemReelStripsConfigLoader } from './config/reel-strips-config/file-system.reel-strips-config-loader';
|
|
20
|
+
import { GameLogicConfigLoader } from './config/game-logic-config/game-logic-config-loader';
|
|
21
|
+
import { GameLogicConfigValidationService } from './validation/game-logic-config-validation.service';
|
|
22
|
+
import { GameRoundType } from './domain/game-round.types';
|
|
23
|
+
import { GameSpinCommand } from './domain/types/game-spin-command';
|
|
24
|
+
import { Logger } from './logger/logger';
|
|
25
|
+
import { ReelStripsConfig } from './domain/reel-strips-config';
|
|
26
|
+
import { ReelStripsConfigLoader } from './config/reel-strips-config/reel-strips-config-loader';
|
|
27
|
+
import { ReelStripsConfigMapper } from './domain/mappers/reel-strips-config.mapper';
|
|
28
|
+
import { ReelStripsConfigValidationService } from './validation/reel-strips-config-validation.service';
|
|
29
|
+
import { RngClientFactory } from './rng/rng-client.factory';
|
|
30
|
+
import { RngService } from './rng/rng-service';
|
|
31
|
+
import { StartBonusRoundCommand } from './domain/types/start-bonus-round-command';
|
|
32
|
+
|
|
33
|
+
export class BonnysFortuneV1GameEngine implements GameEngine<
|
|
34
|
+
BonnysFortunePublicState,
|
|
35
|
+
BonnysFortunePrivateState,
|
|
36
|
+
any
|
|
37
|
+
> {
|
|
38
|
+
private readonly gameCode = 'bonnys-fortune';
|
|
39
|
+
private readonly version = '1.0.0';
|
|
40
|
+
private readonly rtp = 96;
|
|
41
|
+
private readonly gameType = 'slot';
|
|
42
|
+
private readonly gameName = "Bonny's Fortune";
|
|
43
|
+
private readonly provider = 'Omnitronix';
|
|
44
|
+
private config!: BonnysFortuneGameLogicConfigInterface;
|
|
45
|
+
private reels!: Map<GameRoundType, ReelStripsConfig>;
|
|
46
|
+
|
|
47
|
+
readonly configOutPort: GameLogicConfigLoader;
|
|
48
|
+
readonly reelsOutPort: ReelStripsConfigLoader;
|
|
49
|
+
readonly configValidator: GameLogicConfigValidationService;
|
|
50
|
+
readonly reelsValidator: ReelStripsConfigValidationService;
|
|
51
|
+
readonly reelMapper: ReelStripsConfigMapper;
|
|
52
|
+
readonly gameLogic: BonnysFortuneGameLogic;
|
|
53
|
+
readonly rngService: RngService;
|
|
54
|
+
readonly logger: Logger;
|
|
55
|
+
|
|
56
|
+
constructor(config: GameEngineConfig = {}) {
|
|
57
|
+
this.logger = new Logger('BonnysFortuneV1Engine');
|
|
58
|
+
this.configOutPort = new FileSystemGameLogicConfigLoader();
|
|
59
|
+
this.reelsOutPort = new FileSystemReelStripsConfigLoader();
|
|
60
|
+
|
|
61
|
+
const rngClient = RngClientFactory.create({ metrics: config.metrics });
|
|
62
|
+
this.rngService = new RngService(rngClient);
|
|
63
|
+
|
|
64
|
+
this.configValidator = new GameLogicConfigValidationService();
|
|
65
|
+
this.reelsValidator = new ReelStripsConfigValidationService();
|
|
66
|
+
this.reelMapper = new ReelStripsConfigMapper();
|
|
67
|
+
this.gameLogic = new BonnysFortuneGameLogic(this.rngService);
|
|
68
|
+
this.preload();
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
public getGameEngineInfo(): GameEngineInfo {
|
|
72
|
+
return {
|
|
73
|
+
gameCode: this.gameCode,
|
|
74
|
+
version: this.version,
|
|
75
|
+
rtp: this.rtp,
|
|
76
|
+
gameType: this.gameType,
|
|
77
|
+
gameName: this.gameName,
|
|
78
|
+
provider: this.provider,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
private async preload(): Promise<void> {
|
|
83
|
+
this.logger.log('Preloading Bonnys Fortune engine');
|
|
84
|
+
this.configValidator.register(
|
|
85
|
+
this.gameCode,
|
|
86
|
+
new BonnysFortuneGameLogicConfigValidator(),
|
|
87
|
+
);
|
|
88
|
+
const rawConfig =
|
|
89
|
+
await this.configOutPort.loadConfig<BonnysFortuneGameLogicConfigInterface>(
|
|
90
|
+
this.gameCode,
|
|
91
|
+
this.version,
|
|
92
|
+
);
|
|
93
|
+
if (!rawConfig) {
|
|
94
|
+
throw new Error('Bonnys Fortune game logic config not found');
|
|
95
|
+
}
|
|
96
|
+
await this.configValidator.validate(this.gameCode, rawConfig);
|
|
97
|
+
|
|
98
|
+
const reelsCount = rawConfig.baseGame.baseGameConfig.layout.reels;
|
|
99
|
+
const optionsCount =
|
|
100
|
+
rawConfig.baseGame.baseGameConfig.reelsOptions.numberOfOptions;
|
|
101
|
+
|
|
102
|
+
const rawBaseGameReels = await this.reelsOutPort.loadConfig(
|
|
103
|
+
this.gameCode,
|
|
104
|
+
this.version,
|
|
105
|
+
GameRoundType.BASE,
|
|
106
|
+
reelsCount,
|
|
107
|
+
optionsCount,
|
|
108
|
+
);
|
|
109
|
+
await this.reelsValidator.validate(rawBaseGameReels);
|
|
110
|
+
|
|
111
|
+
const rawBonusGame2Reels = await this.reelsOutPort.loadConfig(
|
|
112
|
+
this.gameCode,
|
|
113
|
+
this.version,
|
|
114
|
+
GameRoundType.BONUS,
|
|
115
|
+
reelsCount,
|
|
116
|
+
optionsCount,
|
|
117
|
+
);
|
|
118
|
+
await this.reelsValidator.validate(rawBonusGame2Reels);
|
|
119
|
+
|
|
120
|
+
this.config = rawConfig;
|
|
121
|
+
this.reels = new Map([
|
|
122
|
+
[GameRoundType.BASE, this.reelMapper.mapRawToDomain(rawBaseGameReels)],
|
|
123
|
+
[GameRoundType.BONUS, this.reelMapper.mapRawToDomain(rawBonusGame2Reels)],
|
|
124
|
+
]);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
public async processCommand(
|
|
128
|
+
publicState: BonnysFortunePublicState,
|
|
129
|
+
privateState: BonnysFortunePrivateState,
|
|
130
|
+
command: GameActionCommand,
|
|
131
|
+
): Promise<
|
|
132
|
+
CommandProcessingResult<
|
|
133
|
+
BonnysFortunePublicState,
|
|
134
|
+
BonnysFortunePrivateState,
|
|
135
|
+
any
|
|
136
|
+
>
|
|
137
|
+
> {
|
|
138
|
+
this.logger.log(`Processing command ${command.type}`);
|
|
139
|
+
this.rngService.registerCommand(command.id);
|
|
140
|
+
switch (command.type) {
|
|
141
|
+
case 'INIT_SESSION_STATE': {
|
|
142
|
+
const baseReels = this.reels.get(GameRoundType.BASE);
|
|
143
|
+
if (!baseReels) {
|
|
144
|
+
throw new Error('Base game reels not found');
|
|
145
|
+
}
|
|
146
|
+
const gameConfig = this.resolveInitGameConfig(command.payload ?? {});
|
|
147
|
+
const result = await this.gameLogic.initSessionState(
|
|
148
|
+
this.config,
|
|
149
|
+
baseReels,
|
|
150
|
+
gameConfig,
|
|
151
|
+
command.id,
|
|
152
|
+
);
|
|
153
|
+
const rngOutcome = this.rngService.getRngOutcomeForCommand(command.id);
|
|
154
|
+
this.rngService.removeCommand(command.id);
|
|
155
|
+
return {
|
|
156
|
+
success: true,
|
|
157
|
+
publicState: result.publicState,
|
|
158
|
+
privateState: result.privateState,
|
|
159
|
+
outcome: result,
|
|
160
|
+
message: 'Session state initialized',
|
|
161
|
+
rngOutcome,
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
case 'SPIN': {
|
|
166
|
+
const params = command.payload as GameSpinCommand;
|
|
167
|
+
const baseReels = this.reels.get(GameRoundType.BASE);
|
|
168
|
+
if (!baseReels) {
|
|
169
|
+
throw new Error('Base game reels not found');
|
|
170
|
+
}
|
|
171
|
+
const outcome = await this.gameLogic.spin(
|
|
172
|
+
params,
|
|
173
|
+
publicState,
|
|
174
|
+
privateState,
|
|
175
|
+
this.config,
|
|
176
|
+
baseReels,
|
|
177
|
+
command.id,
|
|
178
|
+
);
|
|
179
|
+
const rngOutcome = this.rngService.getRngOutcomeForCommand(command.id);
|
|
180
|
+
this.rngService.removeCommand(command.id);
|
|
181
|
+
return {
|
|
182
|
+
success: true,
|
|
183
|
+
publicState: outcome.publicState,
|
|
184
|
+
privateState: outcome.privateState,
|
|
185
|
+
outcome,
|
|
186
|
+
message: 'Spin completed',
|
|
187
|
+
rngOutcome,
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
case 'START_BONUS_ROUND': {
|
|
192
|
+
const params = command.payload as StartBonusRoundCommand;
|
|
193
|
+
const bonusReels = this.reels.get(GameRoundType.BONUS);
|
|
194
|
+
if (!bonusReels) {
|
|
195
|
+
throw new Error('Bonus game reels not found');
|
|
196
|
+
}
|
|
197
|
+
const outcome = await this.gameLogic.startBonusRound(
|
|
198
|
+
params,
|
|
199
|
+
publicState,
|
|
200
|
+
privateState,
|
|
201
|
+
this.config,
|
|
202
|
+
bonusReels,
|
|
203
|
+
command.id,
|
|
204
|
+
);
|
|
205
|
+
|
|
206
|
+
if (!outcome) {
|
|
207
|
+
throw new Error('Bonus round outcome not found');
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
const rngOutcome = this.rngService.getRngOutcomeForCommand(command.id);
|
|
211
|
+
this.rngService.removeCommand(command.id);
|
|
212
|
+
return {
|
|
213
|
+
success: true,
|
|
214
|
+
publicState: outcome.publicState,
|
|
215
|
+
privateState: outcome.privateState,
|
|
216
|
+
outcome,
|
|
217
|
+
message: 'Bonus round started',
|
|
218
|
+
rngOutcome,
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
case 'CHEAT_TRIGGER_BONUS': {
|
|
223
|
+
const params = command.payload as CheatTriggerBonusCommand;
|
|
224
|
+
const result = await this.gameLogic.cheatTriggerBonus(
|
|
225
|
+
publicState,
|
|
226
|
+
privateState,
|
|
227
|
+
params,
|
|
228
|
+
);
|
|
229
|
+
|
|
230
|
+
const rngOutcome = this.rngService.getRngOutcomeForCommand(command.id);
|
|
231
|
+
this.rngService.removeCommand(command.id);
|
|
232
|
+
return {
|
|
233
|
+
success: true,
|
|
234
|
+
publicState: result.publicState,
|
|
235
|
+
privateState: result.privateState,
|
|
236
|
+
outcome: result.outcome,
|
|
237
|
+
message: 'Bonus triggered',
|
|
238
|
+
rngOutcome,
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
case 'CHEAT_UPDATE_BONUS_METER_PROGRESS': {
|
|
243
|
+
const params = command.payload as CheatUpdateBonusMeterProgressCommand;
|
|
244
|
+
const result = this.gameLogic.cheatUpdateBonusMeterProgress(
|
|
245
|
+
publicState,
|
|
246
|
+
privateState,
|
|
247
|
+
params,
|
|
248
|
+
this.config,
|
|
249
|
+
);
|
|
250
|
+
|
|
251
|
+
const rngOutcome = this.rngService.getRngOutcomeForCommand(command.id);
|
|
252
|
+
this.rngService.removeCommand(command.id);
|
|
253
|
+
return {
|
|
254
|
+
success: true,
|
|
255
|
+
publicState: result.publicState,
|
|
256
|
+
privateState: result.privateState,
|
|
257
|
+
outcome: result.outcome,
|
|
258
|
+
message: 'Bonus meter progress updated',
|
|
259
|
+
rngOutcome,
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
case 'GET_SYMBOLS': {
|
|
264
|
+
this.rngService.removeCommand(command.id);
|
|
265
|
+
return {
|
|
266
|
+
success: true,
|
|
267
|
+
publicState,
|
|
268
|
+
privateState,
|
|
269
|
+
outcome: this.gameLogic.getSymbols(this.config),
|
|
270
|
+
message: 'Symbols retrieved',
|
|
271
|
+
rngOutcome: undefined,
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
default: {
|
|
276
|
+
return {
|
|
277
|
+
success: false,
|
|
278
|
+
publicState: publicState,
|
|
279
|
+
privateState: privateState,
|
|
280
|
+
outcome: null,
|
|
281
|
+
message: 'Command not found',
|
|
282
|
+
rngOutcome: undefined,
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* Resolves init session params from payload, falling back to game logic config when values are missing or invalid.
|
|
290
|
+
*/
|
|
291
|
+
private resolveInitGameConfig(payload: Record<string, unknown>): {
|
|
292
|
+
betAmountThresholds: number[];
|
|
293
|
+
defaultBetAmount: number;
|
|
294
|
+
} {
|
|
295
|
+
const rawThresholds = payload.betAmountThresholds;
|
|
296
|
+
const validThresholds =
|
|
297
|
+
Array.isArray(rawThresholds) &&
|
|
298
|
+
rawThresholds.length > 0 &&
|
|
299
|
+
rawThresholds.every(
|
|
300
|
+
(n): n is number => typeof n === 'number' && Number.isFinite(n),
|
|
301
|
+
);
|
|
302
|
+
const betAmountThresholds = validThresholds
|
|
303
|
+
? rawThresholds
|
|
304
|
+
: this.config.defaultBetAmountThresholds;
|
|
305
|
+
|
|
306
|
+
const rawDefault = payload.defaultBetAmount ?? this.config.defaultBetAmount;
|
|
307
|
+
const defaultBetAmount =
|
|
308
|
+
typeof rawDefault === 'number' && Number.isFinite(rawDefault)
|
|
309
|
+
? rawDefault
|
|
310
|
+
: this.config.defaultBetAmount;
|
|
311
|
+
|
|
312
|
+
return { betAmountThresholds, defaultBetAmount };
|
|
313
|
+
}
|
|
314
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import * as path from 'path';
|
|
2
|
+
|
|
3
|
+
import { GameLogicConfigLoader } from './game-logic-config-loader';
|
|
4
|
+
export class FileSystemGameLogicConfigLoader implements GameLogicConfigLoader {
|
|
5
|
+
public async loadConfig<T>(
|
|
6
|
+
gameId: string,
|
|
7
|
+
variantId: string,
|
|
8
|
+
): Promise<T | null> {
|
|
9
|
+
try {
|
|
10
|
+
const configPath = path.resolve(__dirname, `./game-logic-config.js`);
|
|
11
|
+
|
|
12
|
+
const configModule = await import(configPath);
|
|
13
|
+
|
|
14
|
+
const config = <T>configModule.gameLogicConfig;
|
|
15
|
+
|
|
16
|
+
if (!config) {
|
|
17
|
+
console.warn(`Config object 'gameConfig' missing in ${configPath}`);
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return config;
|
|
22
|
+
} catch (error) {
|
|
23
|
+
console.warn(`Failed to load config for ${gameId}/${variantId}:`, error);
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|