The UGC API covers replays, screenshots, custom levels, mods, and other player-owned files.
UGC types
| Type | Value | Use for |
|---|---|---|
screenshot | 0 | Still captures |
video | 1 | Clips or recordings |
community | 2 | Levels, mods, maps |
game_managed | 3 | Replays, saves |
other | 4 | Anything else |
Visibility
| Value | Constant | Who sees it |
|---|---|---|
0 | PUBLIC | Everyone |
1 | FRIENDS_ONLY | Friends of the creator |
2 | PRIVATE | The creator only |
Create and upload
Playtest the example project View example project on GitHubfunc _ready():
WavedashSDK.ugc_item_created.connect(_on_ugc_created)
func create_level_share(abs_path: String) -> void:
WavedashSDK.create_ugc_item(
WavedashConstants.UGC_TYPE_COMMUNITY,
"Custom arena",
"Uploaded from editor",
WavedashConstants.UGC_VISIBILITY_PUBLIC,
abs_path
)
func _on_ugc_created(response):
if response.get("success", false):
print("ugc id: ", response["data"])
var result = await Wavedash.SDK.CreateUGCItem(
ugcType: WavedashConstants.UGCItemType.COMMUNITY,
title: "Custom arena",
description: "Uploaded from editor",
visibility: WavedashConstants.UGCVisibility.PUBLIC,
localPath: localPath
);
if (result != null)
Debug.Log($"ugc id: {result["id"]}");
const response = await Wavedash.createUGCItem(
Wavedash.UGCType.COMMUNITY,
"Custom arena",
"Uploaded from editor",
Wavedash.UGCVisibility.PUBLIC,
"ugc/levels/arena.wdc"
);
if (response.success) {
console.log("ugc id:", response.data);
}
In Godot, the file path may be either user://... (auto-normalized) or an absolute path under OS.get_user_data_dir(). Paths outside the user data directory are rejected.
The filePath / localPath argument is optional — you can create a UGC item with metadata only and attach a file later with updateUGCItem. This is useful when the file is still being rendered or generated.
// Create the item first
const response = await Wavedash.createUGCItem(Wavedash.UGCType.COMMUNITY, "Custom arena", "WIP");
// ...game renders the level file...
// Attach the file later
if (response.success) {
const ugcId = response.data;
await Wavedash.updateUGCItem(ugcId, undefined, undefined, undefined, "ugc/levels/arena.wdc");
}
Update an item
WavedashSDK.ugc_item_updated.connect(func(response): print("Updated: ", response.success))
WavedashSDK.update_ugc_item(ugc_id, "Renamed level", "Fixed spawn", WavedashConstants.UGC_VISIBILITY_FRIENDS_ONLY, null)
await Wavedash.SDK.UpdateUGCItem(ugcId, title: "Renamed level", description: "Fixed spawn", visibility: WavedashConstants.UGCVisibility.FRIENDS_ONLY, filePath: null);
await Wavedash.updateUGCItem(ugcId, "Renamed level", "Fixed spawn", Wavedash.UGCVisibility.FRIENDS_ONLY, null);
Download content
func _ready():
WavedashSDK.ugc_item_downloaded.connect(func(response): print("Downloaded: ", response.data))
func download_into_user_data(ugc_id: String, file_name: String) -> void:
var dir = OS.get_user_data_dir() + "/downloads"
DirAccess.make_dir_recursive_absolute(dir)
WavedashSDK.download_ugc_item(ugc_id, dir + "/" + file_name)
var dir = Path.Combine(Application.persistentDataPath, "downloads");
Directory.CreateDirectory(dir);
await Wavedash.SDK.DownloadUGCItem(ugcId, Path.Combine(dir, fileName));
const response = await Wavedash.downloadUGCItem(ugcId, "downloads/pack.bin");
if (response.success) {
const bytes = await Wavedash.readLocalFile("downloads/pack.bin");
}
Attach UGC to a leaderboard entry
Pass the ugcId into the score upload call:
const lb = await Wavedash.getOrCreateLeaderboard(
"speedrun-times",
Wavedash.LeaderboardSortOrder.ASC,
Wavedash.LeaderboardDisplayType.TIME_MILLISECONDS
);
const ugc = await Wavedash.createUGCItem(
Wavedash.UGCType.GAME_MANAGED,
"Replay",
"replay of level 1",
Wavedash.UGCVisibility.PUBLIC,
"replays/run.dat"
);
if (lb.success && ugc.success) {
const ugcId = ugc.data;
await Wavedash.uploadLeaderboardScore(lb.data.id, 12345, true, ugcId);
}