[DX-1425] guide(xp): add project demonstrating player xp and levels - #39
Conversation
| interval: 3s | ||
| timeout: 3s | ||
| retries: 5 | ||
| image: postgres:12.2-alpine |
There was a problem hiding this comment.
I make it a habit of using latest postgres, no real difference for development but since this is for the public I think it's best to show latest, latest is 18
| @@ -0,0 +1,18 @@ | |||
| ARG NAKAMA_VERSION=3.38.0 | |||
| // Sub-achievements are stored in a map, and Go map iteration order is random. | ||
| // Sort level IDs numerically so level_9 always comes before level_10. | ||
| // (A plain alphabetical sort would incorrectly place level_10 before level_2.) | ||
| levelIDs := make([]string, 0, len(playerLevels.SubAchievements)) |
There was a problem hiding this comment.
I don't think there is a need to sort the keys, if you know the format do a for loop and build the key during iteration key = "level_" + i, also works as a check that if the key is not found in the dict there is a level missing or a malformed key
| } | ||
|
|
||
| // isLevelID reports whether id has the expected "level_<N>" format (e.g. "level_1"). | ||
| func isLevelID(id string) bool { |
There was a problem hiding this comment.
I'm not fond of little helper functions like this, id rather inline it
| } | ||
|
|
||
| // grantXPRequest is the JSON payload the client sends when calling rpc_grant_xp. | ||
| type grantXPRequest struct { |
There was a problem hiding this comment.
I like to group struct definitions at the top of the file
| }, | ||
| } | ||
|
|
||
| reward, err := econ.RewardRoll(ctx, logger, nk, userID, rewardConfig) |
There was a problem hiding this comment.
since it's not a weighted reward I think it's simpler to build a hiro.reward object directly and call grant rather than calling roll
Add XP progression guide (Nakama + Hiro + Unity)
Summary
Adds the XP Guide companion project under
Guides/: an end-to-end XP progression system built on Hiro's Achievements and Economy systems, with a Nakama server plugin and a Unity client.The guide demonstrates how to model player levels as sub-achievements driven by an XP currency, so that granting XP anywhere in the game automatically advances the player's level without callers needing to know levelling exists.
How it works
XP is a currency, and levels are sub-achievements of a
player_levelsgroup, each with amax_countequal to the XP required to complete it (100, 200, ... 1000 for levels 1–10).The flow:
rpc_grant_xpwith a base amount.xpcurrency via the Economy reward APIs, so active modifiers (e.g. a double-XP booster) apply.XPLevelPublisherlistens for thecurrencyGrantedevent onxpand advances the level sub-achievements by the actual granted amount.UpdateAchievementscall, so one large grant can complete several levels and partially fill the next.auto_claim, so completing one grants its gem reward immediately.Routing progression through the publisher rather than the RPC means any future XP source gets levelling for free.
The quest group (
defeat_thunder_world) exists to exercise the loop from a realistic source: its sub-quests grant XP, gems, and coins, and one of them grants a 2× XP reward modifier for 120 seconds, which the client surfaces as an active-booster badge.Testing
XPControllerTestspass against a local Nakama instance.defend_the_villageactivates the 2× booster badge and doubles subsequent XP grants.