3.6 KiB
Earnings Center Design
Goal
Add an emulator-owned rewards hub for the "Guadagni" UI. The client and renderer may decide how it looks, but the emulator must own reward amounts, claim eligibility, cooldowns, and anti-abuse checks.
Scope
The first emulator version exposes ten earnings categories:
daily_giftgamesachievementsmarketplacehc_paydaylevel_progressdonationsbonus_bagmystery_boxesclub_job
Every category can be enabled, disabled, configured with one or more reward currencies, and claimed through a single-row claim or a claim-all request. Categories that are not yet backed by a native hotel subsystem still work through static configuration, so the UI contract is stable while deeper integrations are added later.
Architecture
Add a focused com.eu.habbo.habbohotel.earnings package:
EarningsCenterManagerloads category definitions from emulator settings, builds per-user state, and performs claims.EarningsCategoryis the allowlisted category enum and carries the client key.EarningsRewardrepresents one configured reward.EarningsEntryis the serializable row state sent to the client.EarningsClaimResultreports single/all claim outcomes.
The packet layer only parses category keys and delegates to the manager. The client never sends amounts, cooldowns, or reward definitions.
Persistence
Add a database update that creates users_earnings_claims:
iduser_idcategoryperiod_keyclaimed_at- unique key on
user_id,category,period_key
The unique key is the main double-claim guard. period_key is calculated by the emulator from the category cooldown. Daily-style rewards use the UTC date key by default. One-time or long cooldown rows can use the cooldown bucket derived from claimed_at.
Configuration
Add emulator settings with safe defaults:
earnings.enabled=0earnings.<category>.enabled=1earnings.<category>.cooldown.seconds=86400earnings.<category>.credits=0earnings.<category>.pixels=0earnings.<category>.points=0earnings.<category>.points.type=5
The feature defaults off so existing hotels do not receive surprise economy changes after deploying the jar.
Packet Contract
Add three incoming handlers:
RequestEarningsCenterEventClaimEarningsRewardEventClaimAllEarningsRewardsEvent
Add two outgoing composers:
EarningsCenterComposerEarningsClaimResultComposer
Composer format is intentionally simple and renderer-friendly: category key, enabled state, claimable state, next claim timestamp, rewards, and result code. Header IDs must be wired through messages.ini/packet registration in the same style as the rest of the emulator. If the renderer side chooses final IDs later, only the packet mapping should need adjustment.
Security
- Reject unknown category keys.
- Reject all claims when
earnings.enabled=0. - Never trust reward amounts from the client.
- Clamp configured rewards to non-negative values.
- Use the database unique key to prevent concurrent double claims.
claim allprocesses only claimable rows and returns per-category results.
Tests
Add unit tests around the manager-level logic:
- disabled global feature returns disabled rows and rejects claims
- unknown category is rejected
- successful claim grants configured currency once
- duplicate claim in the same period is rejected
- claim-all grants all claimable rows and skips already claimed rows
Packet tests can remain light because renderer IDs may be finalized separately; the critical behavior is the server-side claim guard.