Search documentation

Find pages, sections, and content across all docs.

WavedashDocs

User-generated content

Let players upload, share, and download community-created content

The UGC API covers replays, screenshots, custom levels, mods, and other player-owned files.

UGC types

TypeValueUse for
screenshot0Still captures
video1Clips or recordings
community2Levels, mods, maps
game_managed3Replays, saves
other4Anything else

Visibility

ValueConstantWho sees it
0PUBLICEveryone
1FRIENDS_ONLYFriends of the creator
2PRIVATEThe creator only

Create and upload

Playtest the example project View example project on GitHub
func _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);
}