Repository: Praytic/youtd2 Branch: main Commit: 5471b4b472e7 Files: 3046 Total size: 5.3 MB Directory structure: gitextract_l9w5256r/ ├── .github/ │ ├── CODEOWNERS │ ├── CODE_OF_CONDUCT.md │ ├── CONTRIBUTING.md │ ├── RCLONE.md │ ├── TRANSLATING.md │ └── workflows/ │ └── github-actions-youtd.yml ├── .gitignore ├── LICENSE ├── README.md ├── addons/ │ ├── com.heroiclabs.nakama/ │ │ ├── Nakama.gd │ │ ├── Satori/ │ │ │ ├── NakamaEvent.gd │ │ │ ├── SatoriAPI.gd │ │ │ ├── SatoriClient.gd │ │ │ ├── SatoriHttpAdapter.gd │ │ │ ├── SatoriSession.gd │ │ │ └── utils/ │ │ │ ├── SatoriAsyncResult.gd │ │ │ ├── SatoriException.gd │ │ │ ├── SatoriLogger.gd │ │ │ └── SatoriSerializer.gd │ │ ├── Satori.gd │ │ ├── api/ │ │ │ ├── NakamaAPI.gd │ │ │ ├── NakamaRTAPI.gd │ │ │ ├── NakamaRTMessage.gd │ │ │ ├── NakamaSession.gd │ │ │ ├── NakamaStorageObjectId.gd │ │ │ └── NakamaWriteStorageObject.gd │ │ ├── client/ │ │ │ ├── NakamaClient.gd │ │ │ └── NakamaHTTPAdapter.gd │ │ ├── dotnet-utils/ │ │ │ ├── GodotHttpAdapter.cs │ │ │ ├── GodotLogger.cs │ │ │ └── GodotWebSocketAdapter.cs │ │ ├── socket/ │ │ │ ├── NakamaSocket.gd │ │ │ └── NakamaSocketAdapter.gd │ │ └── utils/ │ │ ├── NakamaAsyncResult.gd │ │ ├── NakamaException.gd │ │ ├── NakamaLogger.gd │ │ ├── NakamaMultiplayerBridge.gd │ │ ├── NakamaMultiplayerPeer.gd │ │ └── NakamaSerializer.gd │ └── nakama-webrtc/ │ ├── LICENSE.txt │ └── OnlineMatch.gd ├── assets/ │ ├── LICENSE.md │ ├── README.md │ ├── creeps/ │ │ ├── README.md │ │ └── orc/ │ │ ├── air/ │ │ │ ├── fly_E.png.import │ │ │ ├── fly_N.png.import │ │ │ ├── fly_NE.png.import │ │ │ ├── fly_NW.png.import │ │ │ ├── fly_S.png.import │ │ │ ├── fly_SE.png.import │ │ │ ├── fly_SW.png.import │ │ │ ├── fly_W.png.import │ │ │ ├── metadata.csv │ │ │ └── metadata.csv.import │ │ ├── boss/ │ │ │ ├── death_E.png.import │ │ │ ├── death_N.png.import │ │ │ ├── death_S.png.import │ │ │ ├── death_W.png.import │ │ │ ├── metadata.csv │ │ │ ├── metadata.csv.import │ │ │ ├── slow_run_E.png.import │ │ │ ├── slow_run_N.png.import │ │ │ ├── slow_run_S.png.import │ │ │ └── slow_run_W.png.import │ │ ├── champion/ │ │ │ ├── death_E.png.import │ │ │ ├── death_N.png.import │ │ │ ├── death_S.png.import │ │ │ ├── death_W.png.import │ │ │ ├── metadata.csv │ │ │ ├── metadata.csv.import │ │ │ ├── slow_run_E.png.import │ │ │ ├── slow_run_N.png.import │ │ │ ├── slow_run_S.png.import │ │ │ └── slow_run_W.png.import │ │ ├── mass/ │ │ │ ├── death_E.png.import │ │ │ ├── death_N.png.import │ │ │ ├── death_S.png.import │ │ │ ├── death_W.png.import │ │ │ ├── metadata.csv │ │ │ ├── metadata.csv.import │ │ │ ├── slow_run_E.png.import │ │ │ ├── slow_run_N.png.import │ │ │ ├── slow_run_S.png.import │ │ │ └── slow_run_W.png.import │ │ └── normal/ │ │ ├── death_E.png.import │ │ ├── death_N.png.import │ │ ├── death_S.png.import │ │ ├── death_W.png.import │ │ ├── metadata.csv │ │ ├── metadata.csv.import │ │ ├── slow_run_E.png.import │ │ ├── slow_run_N.png.import │ │ ├── slow_run_S.png.import │ │ └── slow_run_W.png.import │ ├── effects/ │ │ ├── arrow.png.import │ │ ├── bdragon/ │ │ │ ├── README.md │ │ │ ├── ancient_protector_missile_293.png.import │ │ │ ├── animated_dead_target_102.png.import │ │ │ ├── apply_potion_537.png.import │ │ │ ├── arcane_tower_attack_024.png.import │ │ │ ├── avatar_caster_10.png.import │ │ │ ├── banshee_missile_487.png.import │ │ │ ├── blink_target_720.png.import │ │ │ ├── blood_splatter_376.png.import │ │ │ ├── build_tower_648.png.import │ │ │ ├── charm_target_703.png.import │ │ │ ├── cloud_of_fog_382.png.import │ │ │ ├── cloud_of_fog_small_428.png.import │ │ │ ├── cripple_target_198.png.import │ │ │ ├── crushing_wave_damage_584.png.import │ │ │ ├── crypt_fiend_eggsack_528.png.import │ │ │ ├── cyclone_target_269.png.import │ │ │ ├── death_and_decay_71.png.import │ │ │ ├── death_coil_special_728.png.import │ │ │ ├── devour_509.png.import │ │ │ ├── dispel_magic_target_456.png.import │ │ │ ├── doom_death_451.png.import │ │ │ ├── faerie_dragon_missile_482.png.import │ │ │ ├── firelord_death_explode_77.png.import │ │ │ ├── flame_strike_embers_702.png.import │ │ │ ├── flower_aura_655.png.import │ │ │ ├── frag_boom_spawn_426.png.import │ │ │ ├── freezing_breath_519.png.import │ │ │ ├── frost_armor_damage_474.png.import │ │ │ ├── frost_bolt_missile_063.png.import │ │ │ ├── glaive_746.png.import │ │ │ ├── gold_credit_673.png.import │ │ │ ├── gold_credit_88.png.import │ │ │ ├── healing_wave_target_453.png.import │ │ │ ├── holy_bolt_241.png.import │ │ │ ├── immolation_damage_541.png.import │ │ │ ├── impale_hit_target_529.png.import │ │ │ ├── impale_target_dust_025.png.import │ │ │ ├── incinerate_76.png.import │ │ │ ├── keeper_grove_missile_297.png.import │ │ │ ├── level_up_455.png.import │ │ │ ├── mana_burn_target_388.png.import │ │ │ ├── mana_shield_026.png.import │ │ │ ├── mass_teleport_caster_335.png.import │ │ │ ├── mass_teleport_target_315.png.import │ │ │ ├── mirror_image_caster_711.png.import │ │ │ ├── moonwell_target_584.png.import │ │ │ ├── ne_cancel_death_672.png.import │ │ │ ├── ne_death_612.png.import │ │ │ ├── placeholder_481.png.import │ │ │ ├── polymorph_target_735.png.import │ │ │ ├── projectile_explosion_299.png.import │ │ │ ├── purge_buff_target_195.png.import │ │ │ ├── quillspray_747.png.import │ │ │ ├── raise_skeleton_624.png.import │ │ │ ├── replenish_mana_334.png.import │ │ │ ├── revive_human_623.png.import │ │ │ ├── roar_506.png.import │ │ │ ├── roots_235.png.import │ │ │ ├── shackle_439.png.import │ │ │ ├── shockwave_missile_440.png.import │ │ │ ├── silence_area_611.png.import │ │ │ ├── small_flame_spawn_240.png.import │ │ │ ├── spell_aiil_452.png.import │ │ │ ├── spell_ailb_723.png.import │ │ │ ├── spell_aima_194.png.import │ │ │ ├── spell_aire_174.png.import │ │ │ ├── spell_aiso_295.png.import │ │ │ ├── spell_alim_136.png.import │ │ │ ├── spell_breaker_target_613.png.import │ │ │ ├── spirit_link_target_175.png.import │ │ │ ├── stampede_missile_death_63.png.import │ │ │ ├── starfall_target_257.png.import │ │ │ ├── thunder_clap_466.png.import │ │ │ ├── undead_dissipate_633.png.import │ │ │ ├── upgrade_tower_016.png.import │ │ │ ├── vampiric_aura_199.png.import │ │ │ ├── voodoo_aura_182.png.import │ │ │ ├── warstomp_caster_536.png.import │ │ │ ├── wind_shear_559.png.import │ │ │ ├── wisp_explode_003.png.import │ │ │ ├── ziggurat_frost_missile_325.png.import │ │ │ └── zombify_target_274.png.import │ │ ├── death_explode.png.import │ │ ├── lightning_long.png.import │ │ └── stun.png.import │ ├── fonts/ │ │ ├── Friz Quadrata Std Medium.otf.import │ │ └── NotoSansSC-Medium.ttf.import │ ├── hud/ │ │ ├── bitmaps/ │ │ │ ├── circle_bitmap_100x100.png.import │ │ │ └── circle_bitmap_256x256.png.import │ │ ├── checkbox.png.import │ │ ├── circle_bitmap_256x256.png.import │ │ ├── darkness_face.png.import │ │ ├── element_progress_circle.png.import │ │ ├── horadric_cube.png.import │ │ ├── hud_atlas.png.import │ │ ├── misc2.png.import │ │ ├── misc3.png.import │ │ ├── misc3_s.png.import │ │ ├── misc4.png.import │ │ └── tranquility_face.png.import │ ├── icons/ │ │ ├── amulets.png.import │ │ ├── animals.png.import │ │ ├── armor.png.import │ │ ├── blunt_weapons.png.import │ │ ├── books.png.import │ │ ├── bows.png.import │ │ ├── cannons.png.import │ │ ├── clubs.png.import │ │ ├── creep_icons_atlas.png.import │ │ ├── daggers.png.import │ │ ├── dioramas.png.import │ │ ├── electricity.png.import │ │ ├── elements.png.import │ │ ├── faces.png.import │ │ ├── fire.png.import │ │ ├── food.png.import │ │ ├── furniture.png.import │ │ ├── gems.png.import │ │ ├── generic_icons.png.import │ │ ├── gloves.png.import │ │ ├── helmets.png.import │ │ ├── holy.png.import │ │ ├── hud.png.import │ │ ├── magic.png.import │ │ ├── masks.png.import │ │ ├── mechanical.png.import │ │ ├── misc.png.import │ │ ├── orbs.png.import │ │ ├── plants.png.import │ │ ├── potion_icons_m.png.import │ │ ├── rings.png.import │ │ ├── rockets.png.import │ │ ├── scrolls.png.import │ │ ├── shields.png.import │ │ ├── spears.png.import │ │ ├── statues.png.import │ │ ├── staves.png.import │ │ ├── swords.png.import │ │ ├── tier_icons_m.png.import │ │ ├── tower_icons_m.png.import │ │ ├── tower_variations.png.import │ │ ├── trinkets.png.import │ │ ├── undead.png.import │ │ └── weapons_misc.png.import │ ├── misc/ │ │ ├── autocast_automode_particle.png.import │ │ ├── cast_cursor.png.import │ │ ├── circle_01.png.import │ │ ├── circle_02.png.import │ │ ├── circle_03.png.import │ │ ├── circle_04.png.import │ │ ├── circle_05.png.import │ │ ├── creep_blood_pool.png.import │ │ ├── icob_min.png.import │ │ ├── icob_ultra_wide.png.import │ │ ├── icon.png.import │ │ ├── potion_drop_animation.png.import │ │ └── projectile.png.import │ ├── secrets/ │ │ ├── badwords.csv.import │ │ └── secrets.csv.import │ ├── sfx/ │ │ ├── 161654__qubodup__war-game-battle-level-up-remix.ogg.import │ │ ├── 202147__qubodup__enchant.ogg.import │ │ ├── 411177__silverillusionist__pick-up-item-1-short.ogg.import │ │ ├── 420676__sypherzent__spell-cast-buff-high-tone.ogg.import │ │ ├── 442827__qubodup__fireball.ogg.import │ │ ├── 442903__qubodup__slash-remix.ogg.import │ │ ├── 541478__eminyildirim__magic-fire-spell-impact-punch.ogg.import │ │ ├── 562292__colorscrimsontears__heal-rpg.ogg.import │ │ ├── 649359__sonofxaudio__sword_water01.ogg.import │ │ ├── 665082__sunflora__thud.ogg.import │ │ ├── 682635__bastianhallo__magic-spell.ogg.import │ │ ├── 683044__brettrader__stomp-soft.ogg.import │ │ ├── 714258__qubodup__cloud-poof.ogg.import │ │ ├── 734247__noisyredfox__coins3.ogg.import │ │ ├── 738438__qubodup__orc-grunt-2.ogg.import │ │ ├── 745156__tigreplayz__blast-explosion.ogg.import │ │ ├── DoKashiteru_healspell1.ogg.import │ │ ├── DoKashiteru_healspell2.ogg.import │ │ ├── DoKashiteru_healspell3.ogg.import │ │ ├── README.md │ │ ├── UI_Electric_01.ogg.import │ │ ├── artisticdude_freeze.ogg.import │ │ ├── attribution.csv │ │ ├── attribution.csv.import │ │ ├── bart_foom_0.ogg.import │ │ ├── copyc4t_Archers-shooting.ogg.import │ │ ├── kevl_level_up.ogg.import │ │ ├── little_robot_Spell_00.ogg.import │ │ ├── little_robot_Spell_01.ogg.import │ │ ├── little_robot_UI_Electric_00.ogg.import │ │ ├── little_robot_UI_Electric_01.ogg.import │ │ ├── little_robot_Whoosh_Electric_00.ogg.import │ │ ├── little_robot_Whoosh_Electric_01.ogg.import │ │ ├── little_robot_Whoosh_Electric_02.ogg.import │ │ ├── little_robot_Whoosh_Electric_03.ogg.import │ │ ├── machel_baradari_jump.wav.import │ │ ├── michel_baradari_jump.ogg.import │ │ ├── michel_baradari_jump.wav.import │ │ ├── michel_baradari_lava.ogg.import │ │ ├── michel_baradari_teleport.ogg.import │ │ ├── p0ss_confusion.ogg.import │ │ ├── p0ss_curse5.ogg.import │ │ ├── p0ss_enchant.ogg.import │ │ ├── p0ss_enchant2.ogg.import │ │ ├── p0ss_explode2.ogg.import │ │ ├── p0ss_explode4.ogg.import │ │ ├── p0ss_freeze2.ogg.import │ │ ├── p0ss_heal.ogg.import │ │ ├── p0ss_pestilence.ogg.import │ │ ├── p0ss_sand.ogg.import │ │ ├── p0ss_shot.ogg.import │ │ ├── p0ss_spell.ogg.import │ │ ├── p0ss_steam.ogg.import │ │ ├── p0ss_teleport.ogg.import │ │ ├── p0ss_warp.ogg.import │ │ ├── p0ss_warp2.ogg.import │ │ ├── p0ss_warp3.ogg.import │ │ ├── p0ss_water.ogg.import │ │ ├── p0ss_zap.ogg.import │ │ ├── p0ss_zap10.ogg.import │ │ ├── p0ss_zap5a.ogg.import │ │ ├── spit_01.ogg.import │ │ ├── unknown_attack_sound1.ogg.import │ │ ├── unknown_fire_attack1.ogg.import │ │ ├── unknown_iceball.ogg.import │ │ ├── unknown_iron_attack1.ogg.import │ │ ├── unknown_swosh-08.ogg.import │ │ └── unknown_swosh-11.ogg.import │ ├── texts/ │ │ ├── README.md │ │ ├── texts.csv.import │ │ ├── texts_history.csv │ │ └── texts_history.csv.import │ ├── tiles/ │ │ ├── License.txt │ │ ├── README.md │ │ ├── barrels.png.import │ │ ├── bridges.png.import │ │ ├── chairs.png.import │ │ ├── chests.png.import │ │ ├── empty_tiles.png.import │ │ ├── floor.png.import │ │ ├── floor1_walls.png.import │ │ ├── planks.png.import │ │ ├── spiral_stairs.png.import │ │ ├── stairs.png.import │ │ ├── stone_columns.png.import │ │ ├── stone_walls.png.import │ │ ├── tables.png.import │ │ ├── wooden_pile.png.import │ │ └── wooden_supports.png.import │ ├── tiles_decoration/ │ │ ├── bushes1.png.import │ │ ├── column_overgrown.png.import │ │ ├── leaves1.png.import │ │ ├── stones1.png.import │ │ ├── stones2.png.import │ │ ├── stones3.png.import │ │ ├── trees1.png.import │ │ ├── trees2.png.import │ │ ├── vines1.png.import │ │ ├── vines2.png.import │ │ ├── vines3.png.import │ │ ├── vines4.png.import │ │ ├── vines5.png.import │ │ ├── vines6.png.import │ │ ├── vines7.png.import │ │ └── wall_vines1.png.import │ ├── tower_sprites/ │ │ ├── astral_common.png.import │ │ ├── astral_rare.png.import │ │ ├── astral_uncommon.png.import │ │ ├── astral_unique.png.import │ │ ├── darkness_common.png.import │ │ ├── darkness_rare.png.import │ │ ├── darkness_uncommon.png.import │ │ ├── darkness_unique.png.import │ │ ├── fire_common.png.import │ │ ├── fire_rare.png.import │ │ ├── fire_uncommon.png.import │ │ ├── fire_unique.png.import │ │ ├── ice_common.png.import │ │ ├── ice_rare.png.import │ │ ├── ice_uncommon.png.import │ │ ├── ice_unique.png.import │ │ ├── iron_common.png.import │ │ ├── iron_rare.png.import │ │ ├── iron_uncommon.png.import │ │ ├── iron_unique.png.import │ │ ├── nature_common.png.import │ │ ├── nature_rare.png.import │ │ ├── nature_uncommon.png.import │ │ ├── nature_unique.png.import │ │ ├── storm_common.png.import │ │ ├── storm_rare.png.import │ │ ├── storm_uncommon.png.import │ │ └── storm_unique.png.import │ └── tutorial_pictures/ │ ├── README.md │ ├── build_tower.png.import │ ├── item_drop.png.import │ ├── research.png.import │ ├── research_element.png.import │ ├── resource_panel.png.import │ ├── roll_towers.png.import │ ├── tower_info.png.import │ ├── tower_level.png.import │ ├── tower_mouse_over.png.import │ ├── tower_stash.png.import │ ├── transform.png.import │ ├── upgrade.png.import │ └── wave_finish.png.import ├── build/ │ ├── README.md │ ├── linux/ │ │ └── README.md │ ├── macos/ │ │ └── README.md │ ├── web/ │ │ └── full-size.html │ └── windows/ │ └── README.md ├── data/ │ ├── README.md │ ├── ability_properties.csv │ ├── ability_properties.csv.import │ ├── aura_properties.csv │ ├── aura_properties.csv.import │ ├── autocast_properties.csv │ ├── autocast_properties.csv.import │ ├── builder_properties.csv │ ├── builder_properties.csv.import │ ├── exp_for_level.csv │ ├── exp_for_level.csv.import │ ├── hints/ │ │ ├── advanced.csv │ │ ├── advanced.csv.import │ │ ├── basic.csv.import │ │ ├── basics.csv │ │ ├── basics.csv.import │ │ ├── chat_commands.csv │ │ ├── chat_commands.csv.import │ │ ├── creep_specials.csv │ │ ├── creep_specials.csv.import │ │ ├── creeps.csv │ │ ├── creeps.csv.import │ │ ├── items.csv │ │ ├── items.csv.import │ │ ├── towers.csv │ │ ├── towers.csv.import │ │ ├── tutorial.csv │ │ └── tutorial.csv.import │ ├── item_properties.csv │ ├── item_properties.csv.import │ ├── mission_properties.csv │ ├── mission_properties.csv.import │ ├── player_exp_for_level.csv │ ├── player_exp_for_level.csv.import │ ├── recipe_properties.csv │ ├── recipe_properties.csv.import │ ├── tower_properties.csv │ ├── tower_properties.csv.import │ ├── wave_special_properties.csv │ ├── wave_special_properties.csv.import │ ├── wisdom_upgrades.csv │ └── wisdom_upgrades.csv.import ├── export_presets.cfg ├── project.godot ├── rclone-filter ├── resources/ │ ├── README.md │ ├── button_groups/ │ │ ├── element_filter_button_group.tres │ │ ├── interface_size_setting_button_group.tres │ │ ├── item_type_filter_button_group.tres │ │ ├── menu_cards_button_group.tres │ │ ├── right_panel_button_group.tres │ │ └── speed_button_group.tres │ ├── icons/ │ │ ├── amulets/ │ │ │ ├── amulet_01.tres │ │ │ ├── amulet_02.tres │ │ │ ├── amulet_03.tres │ │ │ └── amulet_04.tres │ │ ├── animals/ │ │ │ ├── bat_01.tres │ │ │ ├── bat_02.tres │ │ │ ├── bat_03.tres │ │ │ ├── cow.tres │ │ │ ├── dragon_01.tres │ │ │ ├── dragon_02.tres │ │ │ ├── dragon_03.tres │ │ │ ├── dragon_04.tres │ │ │ ├── dragon_05.tres │ │ │ ├── fish_01.tres │ │ │ ├── fish_02.tres │ │ │ ├── fish_03.tres │ │ │ ├── fish_04.tres │ │ │ ├── mech_toad.tres │ │ │ ├── piglet_warrior.tres │ │ │ ├── rabbit.tres │ │ │ ├── rooster_warrior.tres │ │ │ ├── spider_01.tres │ │ │ ├── spider_02.tres │ │ │ ├── spider_03.tres │ │ │ └── wing.tres │ │ ├── armor/ │ │ │ ├── chest_plate.tres │ │ │ ├── chest_plate_with_spike.tres │ │ │ ├── coat.tres │ │ │ ├── lightning_boot.tres │ │ │ ├── vest_01.tres │ │ │ ├── vest_02.tres │ │ │ ├── vest_03.tres │ │ │ └── vest_04.tres │ │ ├── blunt_weapons/ │ │ │ ├── hammer_01.tres │ │ │ ├── hammer_02.tres │ │ │ ├── hammer_03.tres │ │ │ ├── hammer_04.tres │ │ │ ├── wood_01.tres │ │ │ ├── wood_02.tres │ │ │ ├── wood_03.tres │ │ │ └── wood_04.tres │ │ ├── books/ │ │ │ ├── book_01.tres │ │ │ ├── book_02.tres │ │ │ ├── book_03.tres │ │ │ ├── book_04.tres │ │ │ ├── book_05.tres │ │ │ ├── book_06.tres │ │ │ ├── book_07.tres │ │ │ ├── book_08.tres │ │ │ ├── book_09.tres │ │ │ ├── book_10.tres │ │ │ ├── book_11.tres │ │ │ ├── note_01.tres │ │ │ ├── note_02.tres │ │ │ ├── note_03.tres │ │ │ └── note_04.tres │ │ ├── bows/ │ │ │ ├── arrow_01.tres │ │ │ ├── arrow_02.tres │ │ │ ├── bow_01.tres │ │ │ ├── bow_02.tres │ │ │ ├── bow_03.tres │ │ │ ├── bow_04.tres │ │ │ ├── bow_05.tres │ │ │ └── bow_06.tres │ │ ├── cannons/ │ │ │ ├── cannon_01.tres │ │ │ ├── cannon_02.tres │ │ │ ├── cannon_03.tres │ │ │ ├── cannon_04.tres │ │ │ ├── cannon_05.tres │ │ │ ├── cannon_06.tres │ │ │ ├── cannon_07.tres │ │ │ ├── cannon_08.tres │ │ │ ├── gun_01.tres │ │ │ └── gun_02.tres │ │ ├── clubs/ │ │ │ ├── club_01.tres │ │ │ ├── club_02.tres │ │ │ ├── club_03.tres │ │ │ ├── club_04.tres │ │ │ ├── club_04_without_handle.tres │ │ │ └── club_glowing.tres │ │ ├── creep_icons/ │ │ │ ├── challenge_challenge_boss.tres │ │ │ ├── challenge_challenge_mass.tres │ │ │ ├── humanoid_air.tres │ │ │ ├── humanoid_boss.tres │ │ │ ├── humanoid_champion.tres │ │ │ ├── humanoid_mass.tres │ │ │ ├── humanoid_normal.tres │ │ │ ├── magic_air.tres │ │ │ ├── magic_boss.tres │ │ │ ├── magic_champion.tres │ │ │ ├── magic_mass.tres │ │ │ ├── magic_normal.tres │ │ │ ├── nature_air.tres │ │ │ ├── nature_boss.tres │ │ │ ├── nature_champion.tres │ │ │ ├── nature_mass.tres │ │ │ ├── nature_normal.tres │ │ │ ├── orc_air.tres │ │ │ ├── orc_boss.tres │ │ │ ├── orc_champion.tres │ │ │ ├── orc_mass.tres │ │ │ ├── orc_normal.tres │ │ │ ├── undead_air.tres │ │ │ ├── undead_boss.tres │ │ │ ├── undead_champion.tres │ │ │ ├── undead_mass.tres │ │ │ └── undead_normal.tres │ │ ├── daggers/ │ │ │ ├── dagger_01.tres │ │ │ ├── dagger_02.tres │ │ │ ├── dagger_03.tres │ │ │ ├── dagger_04.tres │ │ │ ├── dagger_05.tres │ │ │ ├── dagger_06.tres │ │ │ ├── dagger_07.tres │ │ │ ├── dagger_08.tres │ │ │ └── dagger_09.tres │ │ ├── dioramas/ │ │ │ ├── book_display.tres │ │ │ ├── church.tres │ │ │ ├── fountain.tres │ │ │ ├── mountain.tres │ │ │ └── pyramid.tres │ │ ├── electricity/ │ │ │ ├── electricity_blue.tres │ │ │ ├── electricity_yellow.tres │ │ │ ├── lightning_circle_cyan.tres │ │ │ ├── lightning_circle_white.tres │ │ │ ├── lightning_glowing.tres │ │ │ └── thunderstorm.tres │ │ ├── elements/ │ │ │ ├── astral.tres │ │ │ ├── darkness.tres │ │ │ ├── fire.tres │ │ │ ├── ice.tres │ │ │ ├── iron.tres │ │ │ ├── nature.tres │ │ │ └── storm.tres │ │ ├── faces/ │ │ │ ├── green_demon.tres │ │ │ ├── man_01.tres │ │ │ ├── man_02.tres │ │ │ ├── man_03.tres │ │ │ ├── man_04.tres │ │ │ ├── man_05.tres │ │ │ ├── mech_zombie.tres │ │ │ ├── orc_01.tres │ │ │ ├── orc_02.tres │ │ │ ├── sleeping_leaf_spirit.tres │ │ │ ├── woman_01.tres │ │ │ ├── woman_02.tres │ │ │ └── woman_03.tres │ │ ├── fire/ │ │ │ ├── burning_cloth.tres │ │ │ ├── fire_bowl_01.tres │ │ │ ├── fire_bowl_02.tres │ │ │ ├── fire_bowl_03.tres │ │ │ ├── fire_in_cup.tres │ │ │ ├── flame_blue.tres │ │ │ ├── flame_blue_glowing.tres │ │ │ ├── flame_purple.tres │ │ │ └── torch.tres │ │ ├── food/ │ │ │ ├── barrel.tres │ │ │ ├── beer_01.tres │ │ │ ├── beer_02.tres │ │ │ ├── lard.tres │ │ │ ├── pork.tres │ │ │ ├── pork_without_plate.tres │ │ │ └── poultry.tres │ │ ├── furniture/ │ │ │ ├── artifact_of_skadi.tres │ │ │ ├── artifact_on_pedestal.tres │ │ │ ├── bed.tres │ │ │ ├── chest.tres │ │ │ ├── exploding_mirror.tres │ │ │ ├── furniture.tres │ │ │ ├── stool.tres │ │ │ ├── throne.tres │ │ │ └── wooden_stand_with_nail.tres │ │ ├── gems/ │ │ │ ├── crystal.tres │ │ │ ├── earring_01.tres │ │ │ ├── earring_02.tres │ │ │ ├── earring_03.tres │ │ │ ├── earring_04.tres │ │ │ ├── earring_05.tres │ │ │ ├── gem_01.tres │ │ │ ├── gem_02.tres │ │ │ ├── gem_03.tres │ │ │ ├── gem_04.tres │ │ │ ├── gem_05.tres │ │ │ ├── gem_06.tres │ │ │ ├── gem_07.tres │ │ │ ├── stone_01.tres │ │ │ ├── stone_02.tres │ │ │ ├── stone_03.tres │ │ │ └── stone_04.tres │ │ ├── generic_icons/ │ │ │ ├── README.md │ │ │ ├── abdominal_armor.tres │ │ │ ├── alien_skull.tres │ │ │ ├── alligator_clip.tres │ │ │ ├── amber_mosquito.tres │ │ │ ├── angel_outfit.tres │ │ │ ├── angel_wings.tres │ │ │ ├── animal_skull.tres │ │ │ ├── ankh.tres │ │ │ ├── aquarius.tres │ │ │ ├── aries.tres │ │ │ ├── armor_vest.tres │ │ │ ├── atomic_slashes.tres │ │ │ ├── azul_flake.tres │ │ │ ├── barbute.tres │ │ │ ├── barefoot.tres │ │ │ ├── bat_mask.tres │ │ │ ├── beard.tres │ │ │ ├── biceps.tres │ │ │ ├── burning_dot.tres │ │ │ ├── burning_meteor.tres │ │ │ ├── charm.tres │ │ │ ├── chest_armor.tres │ │ │ ├── cog.tres │ │ │ ├── egg.tres │ │ │ ├── electric.tres │ │ │ ├── energy_breath.tres │ │ │ ├── fire_dash.tres │ │ │ ├── flame.tres │ │ │ ├── foot_trip.tres │ │ │ ├── ghost.tres │ │ │ ├── gold_bar.tres │ │ │ ├── hammer_drop.tres │ │ │ ├── holy_grail.tres │ │ │ ├── horned_helm.tres │ │ │ ├── hourglass.tres │ │ │ ├── knocked_out_stars.tres │ │ │ ├── liberty_wing.tres │ │ │ ├── meat.tres │ │ │ ├── mighty_force.tres │ │ │ ├── mine_explosion.tres │ │ │ ├── moebius_trefoil.tres │ │ │ ├── omega.tres │ │ │ ├── open_wound.tres │ │ │ ├── ophiucus.tres │ │ │ ├── orc_head.tres │ │ │ ├── over_infinity.tres │ │ │ ├── perpendicular_rings.tres │ │ │ ├── pisces.tres │ │ │ ├── poison_gas.tres │ │ │ ├── pokecog.tres │ │ │ ├── polar_star.tres │ │ │ ├── rolling_energy.tres │ │ │ ├── root_tip.tres │ │ │ ├── round_potion.tres │ │ │ ├── rss.tres │ │ │ ├── semi_closed_eye.tres │ │ │ ├── shiny_omega.tres │ │ │ ├── spell_book.tres │ │ │ ├── spider_web.tres │ │ │ ├── sprint.tres │ │ │ ├── star_swirl.tres │ │ │ ├── triple_scratches.tres │ │ │ ├── turtle_shell.tres │ │ │ └── wolf_howl.tres │ │ ├── gloves/ │ │ │ ├── curse.tres │ │ │ ├── gloves_01.tres │ │ │ ├── gloves_02.tres │ │ │ ├── gloves_03.tres │ │ │ ├── gloves_04.tres │ │ │ ├── gloves_05.tres │ │ │ ├── gloves_06.tres │ │ │ ├── gloves_07.tres │ │ │ ├── gloves_08.tres │ │ │ ├── heal.tres │ │ │ └── steal.tres │ │ ├── helmets/ │ │ │ ├── crown_01.tres │ │ │ ├── crown_02.tres │ │ │ ├── crown_03.tres │ │ │ ├── helmet_01.tres │ │ │ ├── helmet_02.tres │ │ │ ├── helmet_03.tres │ │ │ ├── helmet_04.tres │ │ │ ├── helmet_05.tres │ │ │ ├── helmet_06.tres │ │ │ ├── helmet_07.tres │ │ │ ├── helmet_08.tres │ │ │ ├── wizard_hat_01.tres │ │ │ └── wizard_hat_02.tres │ │ ├── holy/ │ │ │ ├── altar.tres │ │ │ ├── axe.tres │ │ │ ├── cross_01.tres │ │ │ ├── cross_02.tres │ │ │ ├── cross_03.tres │ │ │ ├── orb.tres │ │ │ ├── tombstone.tres │ │ │ └── white_trinket.tres │ │ ├── hud/ │ │ │ ├── dice.tres │ │ │ ├── gem_all_rarities.tres │ │ │ ├── gem_common.tres │ │ │ ├── gem_rare.tres │ │ │ ├── gem_uncommon.tres │ │ │ ├── gem_unique.tres │ │ │ ├── gold.tres │ │ │ ├── item_stash.tres │ │ │ ├── knowledge_tome.tres │ │ │ ├── oils.tres │ │ │ ├── recipe_distill.tres │ │ │ ├── recipe_perfect.tres │ │ │ ├── recipe_reassemble.tres │ │ │ ├── recipe_rebrew.tres │ │ │ ├── research_elements.tres │ │ │ ├── tower_food.tres │ │ │ └── tower_stash.tres │ │ ├── magic/ │ │ │ ├── bowl_01.tres │ │ │ ├── bowl_02.tres │ │ │ ├── claw_01.tres │ │ │ ├── claw_02.tres │ │ │ ├── claw_03.tres │ │ │ ├── claw_04.tres │ │ │ ├── eye.tres │ │ │ ├── eye_blue.tres │ │ │ ├── eyes_many.tres │ │ │ ├── fire.tres │ │ │ ├── lantern.tres │ │ │ ├── lock_01.tres │ │ │ ├── lock_02.tres │ │ │ ├── lock_03.tres │ │ │ ├── lock_04.tres │ │ │ ├── magic_stone.tres │ │ │ └── magic_stone_green.tres │ │ ├── masks/ │ │ │ ├── mask_01.tres │ │ │ ├── mask_02.tres │ │ │ ├── mask_03.tres │ │ │ ├── mask_04.tres │ │ │ ├── mask_05.tres │ │ │ ├── mask_06.tres │ │ │ └── mask_07.tres │ │ ├── mechanical/ │ │ │ ├── alchemy_kit_01.tres │ │ │ ├── alchemy_kit_02.tres │ │ │ ├── alchemy_kit_03.tres │ │ │ ├── battery.tres │ │ │ ├── black_gold_calculator.tres │ │ │ ├── circuit_board.tres │ │ │ ├── compass.tres │ │ │ ├── factory_fumes.tres │ │ │ ├── factory_gears.tres │ │ │ ├── gold_machine.tres │ │ │ ├── lamp.tres │ │ │ ├── mech_badge.tres │ │ │ └── mech_umbrella.tres │ │ ├── misc/ │ │ │ ├── balls_01.tres │ │ │ ├── balls_02.tres │ │ │ ├── balls_03.tres │ │ │ ├── cauldron.tres │ │ │ ├── flag_01.tres │ │ │ ├── flag_02.tres │ │ │ ├── flag_03.tres │ │ │ ├── gold_cart.tres │ │ │ ├── grenade.tres │ │ │ ├── poison_01.tres │ │ │ ├── poison_02.tres │ │ │ ├── quiver.tres │ │ │ ├── red_knight.tres │ │ │ ├── spiky_totem_01.tres │ │ │ ├── spiky_totem_02.tres │ │ │ ├── teapot_01.tres │ │ │ ├── teapot_02.tres │ │ │ ├── teapot_03.tres │ │ │ ├── teapot_04.tres │ │ │ ├── toolbox_01.tres │ │ │ └── toolbox_02.tres │ │ ├── orbs/ │ │ │ ├── moon.tres │ │ │ ├── orb_fire.tres │ │ │ ├── orb_green.tres │ │ │ ├── orb_ice.tres │ │ │ ├── orb_ice_melting.tres │ │ │ ├── orb_molten.tres │ │ │ ├── orb_molten_dull.tres │ │ │ ├── orb_shadow.tres │ │ │ └── orb_small.tres │ │ ├── plants/ │ │ │ ├── branch_01.tres │ │ │ ├── branch_02.tres │ │ │ ├── flower_01.tres │ │ │ ├── flower_02.tres │ │ │ ├── flower_03.tres │ │ │ ├── flower_04.tres │ │ │ ├── flower_05.tres │ │ │ ├── flower_06.tres │ │ │ ├── leaf_01.tres │ │ │ ├── leaf_02.tres │ │ │ ├── leaf_03.tres │ │ │ ├── plant_in_pot.tres │ │ │ ├── stump.tres │ │ │ └── tree.tres │ │ ├── potions/ │ │ │ ├── beaker_01.tres │ │ │ ├── beaker_02.tres │ │ │ ├── beaker_03.tres │ │ │ ├── potion_01.tres │ │ │ ├── potion_02.tres │ │ │ ├── potion_03.tres │ │ │ ├── potion_04.tres │ │ │ ├── potion_05.tres │ │ │ ├── potion_06.tres │ │ │ ├── potion_07.tres │ │ │ ├── potion_08.tres │ │ │ ├── potion_09.tres │ │ │ ├── potion_10.tres │ │ │ ├── potion_blue_01.tres │ │ │ ├── potion_blue_02.tres │ │ │ ├── potion_blue_03.tres │ │ │ ├── potion_blue_04.tres │ │ │ ├── potion_blue_05.tres │ │ │ ├── potion_cyan_01.tres │ │ │ ├── potion_cyan_02.tres │ │ │ ├── potion_cyan_03.tres │ │ │ ├── potion_green_01.tres │ │ │ ├── potion_green_02.tres │ │ │ ├── potion_green_03.tres │ │ │ ├── potion_heart_01.tres │ │ │ ├── potion_heart_02.tres │ │ │ ├── potion_orange_01.tres │ │ │ ├── potion_orange_02.tres │ │ │ ├── potion_orange_03.tres │ │ │ ├── potion_purple_01.tres │ │ │ ├── potion_purple_02.tres │ │ │ ├── potion_purple_03.tres │ │ │ ├── potion_red_01.tres │ │ │ ├── potion_red_02.tres │ │ │ ├── potion_red_03.tres │ │ │ ├── potion_yellow_01.tres │ │ │ └── potion_yellow_02.tres │ │ ├── rings/ │ │ │ ├── ring_01.tres │ │ │ ├── ring_02.tres │ │ │ ├── ring_03.tres │ │ │ ├── ring_04.tres │ │ │ ├── ring_05.tres │ │ │ ├── ring_06.tres │ │ │ ├── ring_07.tres │ │ │ └── ring_08.tres │ │ ├── rockets/ │ │ │ ├── rocket_01.tres │ │ │ ├── rocket_02.tres │ │ │ ├── rocket_03.tres │ │ │ ├── rocket_04.tres │ │ │ ├── rocket_05.tres │ │ │ ├── rocket_06.tres │ │ │ └── rocket_07.tres │ │ ├── scrolls/ │ │ │ ├── scroll_01.tres │ │ │ ├── scroll_02.tres │ │ │ ├── scroll_03.tres │ │ │ ├── scroll_04.tres │ │ │ ├── scroll_05.tres │ │ │ ├── scroll_06.tres │ │ │ ├── scroll_07.tres │ │ │ └── scroll_08.tres │ │ ├── shields/ │ │ │ ├── shield_bronze.tres │ │ │ ├── shield_castle.tres │ │ │ ├── shield_gray_with_gold_emblem.tres │ │ │ ├── shield_green.tres │ │ │ ├── shield_skull.tres │ │ │ ├── shield_white.tres │ │ │ ├── shield_with_emblem.tres │ │ │ ├── shield_with_gold_helmet.tres │ │ │ ├── shield_wood.tres │ │ │ ├── shield_wood_small.tres │ │ │ └── shield_wood_small_glowing.tres │ │ ├── spears/ │ │ │ ├── many_spears_01.tres │ │ │ ├── many_spears_02.tres │ │ │ ├── spear_01.tres │ │ │ ├── spear_02.tres │ │ │ └── spear_03.tres │ │ ├── statues/ │ │ │ ├── statue_01.tres │ │ │ ├── statue_02.tres │ │ │ ├── statue_03.tres │ │ │ ├── statue_dwarf_01.tres │ │ │ ├── statue_dwarf_02.tres │ │ │ └── statue_warrior.tres │ │ ├── staves/ │ │ │ ├── staff_01.tres │ │ │ ├── staff_02.tres │ │ │ ├── staff_03.tres │ │ │ ├── staff_04.tres │ │ │ ├── staff_05.tres │ │ │ ├── staff_06.tres │ │ │ ├── staff_07.tres │ │ │ ├── wand_01.tres │ │ │ ├── wand_02.tres │ │ │ ├── wand_03.tres │ │ │ ├── wand_04.tres │ │ │ ├── wand_05.tres │ │ │ └── wand_glowing.tres │ │ ├── swords/ │ │ │ ├── greatsword_01.tres │ │ │ ├── greatsword_02.tres │ │ │ ├── greatsword_03.tres │ │ │ ├── greatsword_04.tres │ │ │ ├── greatsword_05.tres │ │ │ ├── sword_01.tres │ │ │ ├── sword_02.tres │ │ │ ├── sword_03.tres │ │ │ ├── sword_swing_01.tres │ │ │ └── sword_swing_02.tres │ │ ├── tier_icons/ │ │ │ ├── common_1.tres │ │ │ ├── common_2.tres │ │ │ ├── common_3.tres │ │ │ ├── common_4.tres │ │ │ ├── common_5.tres │ │ │ ├── common_6.tres │ │ │ ├── common_7.tres │ │ │ ├── rare_1.tres │ │ │ ├── rare_2.tres │ │ │ ├── rare_3.tres │ │ │ ├── rare_4.tres │ │ │ ├── rare_5.tres │ │ │ ├── rare_6.tres │ │ │ ├── rare_7.tres │ │ │ ├── uncommon_1.tres │ │ │ ├── uncommon_2.tres │ │ │ ├── uncommon_3.tres │ │ │ ├── uncommon_4.tres │ │ │ ├── uncommon_5.tres │ │ │ ├── uncommon_6.tres │ │ │ ├── uncommon_7.tres │ │ │ ├── unique_1.tres │ │ │ ├── unique_2.tres │ │ │ ├── unique_3.tres │ │ │ ├── unique_4.tres │ │ │ ├── unique_5.tres │ │ │ ├── unique_6.tres │ │ │ └── unique_7.tres │ │ ├── tower_icons/ │ │ │ ├── abandoned_pit.tres │ │ │ ├── abominable_snowman.tres │ │ │ ├── afflicted_obelisk.tres │ │ │ ├── ancient_energy_converter.tres │ │ │ ├── annoyed_tree.tres │ │ │ ├── arcane_storm.tres │ │ │ ├── area_roaster.tres │ │ │ ├── ash_geyser.tres │ │ │ ├── astral_lantern.tres │ │ │ ├── astral_rift.tres │ │ │ ├── baby_plant.tres │ │ │ ├── baby_tuskin.tres │ │ │ ├── ball_lightning_accelerator.tres │ │ │ ├── basic_knowledge.tres │ │ │ ├── black_dragon_roost.tres │ │ │ ├── black_rock_totem.tres │ │ │ ├── bomb_turret.tres │ │ │ ├── bone_shrine.tres │ │ │ ├── bonk_the_living_mountain.tres │ │ │ ├── broken_cage.tres │ │ │ ├── broken_circle_of_wind.tres │ │ │ ├── broken_fire_pit.tres │ │ │ ├── broken_lightning_rod.tres │ │ │ ├── bronze_dragon_roost.tres │ │ │ ├── buried_soul.tres │ │ │ ├── burning_watchtower.tres │ │ │ ├── burrow.tres │ │ │ ├── caged_fire.tres │ │ │ ├── cenarion.tres │ │ │ ├── chaining_storm.tres │ │ │ ├── chaos_warlock.tres │ │ │ ├── charged_obelisk.tres │ │ │ ├── chilled_spire.tres │ │ │ ├── cloud_warrior.tres │ │ │ ├── cloudy_temple_of_absorption.tres │ │ │ ├── coconut_sapling.tres │ │ │ ├── coin_machine.tres │ │ │ ├── cold_obelisk.tres │ │ │ ├── cold_troll.tres │ │ │ ├── contraption.tres │ │ │ ├── crimson_wyrm.tres │ │ │ ├── cruel_fire.tres │ │ │ ├── cursed_grounds.tres │ │ │ ├── cute_small_spider.tres │ │ │ ├── dark_battery.tres │ │ │ ├── dark_fire_pit.tres │ │ │ ├── death_knight.tres │ │ │ ├── dimensional_flux_collector.tres │ │ │ ├── dragon_sorcerer.tres │ │ │ ├── drake_whisperer.tres │ │ │ ├── dreadlord.tres │ │ │ ├── dutchmans_grave.tres │ │ │ ├── dwarven_forgery.tres │ │ │ ├── ebonfrost_crystal.tres │ │ │ ├── embershell_turtle_hatchling.tres │ │ │ ├── energy_junction.tres │ │ │ ├── essence_of_fury.tres │ │ │ ├── felweed.tres │ │ │ ├── fenced_flames.tres │ │ │ ├── fiery_dog.tres │ │ │ ├── fiery_pebble.tres │ │ │ ├── fire_battery.tres │ │ │ ├── fire_star.tres │ │ │ ├── firestorm_cell.tres │ │ │ ├── fisherman.tres │ │ │ ├── forest_archer.tres │ │ │ ├── forest_protectress.tres │ │ │ ├── forest_troll.tres │ │ │ ├── frost_root.tres │ │ │ ├── frosty_rock.tres │ │ │ ├── frozen_well.tres │ │ │ ├── garden_of_eden.tres │ │ │ ├── gatling_gun.tres │ │ │ ├── genis_sage.tres │ │ │ ├── geothermal_extractor.tres │ │ │ ├── glaive_master.tres │ │ │ ├── glowing_solar_orb.tres │ │ │ ├── gnoll_thunder_mage.tres │ │ │ ├── goblin_stronghold.tres │ │ │ ├── grab-o-bot.tres │ │ │ ├── green_dragon_roost.tres │ │ │ ├── green_lightning.tres │ │ │ ├── greyfang.tres │ │ │ ├── gryphon_rider.tres │ │ │ ├── hall_of_souls.tres │ │ │ ├── harby.tres │ │ │ ├── harpy_witch.tres │ │ │ ├── haunted_rubble.tres │ │ │ ├── healing_obelisk.tres │ │ │ ├── helicopter_zone.tres │ │ │ ├── hell_bat.tres │ │ │ ├── holy_energy.tres │ │ │ ├── ice_battery.tres │ │ │ ├── icy_core.tres │ │ │ ├── icy_skulls.tres │ │ │ ├── icy_spirit.tres │ │ │ ├── igloo.tres │ │ │ ├── inexperienced_huntress.tres │ │ │ ├── inflamed_stone.tres │ │ │ ├── initiate_elementalist.tres │ │ │ ├── it.tres │ │ │ ├── jungle_stalker.tres │ │ │ ├── kraken.tres │ │ │ ├── lesser_astral_defender.tres │ │ │ ├── lesser_dark_defender.tres │ │ │ ├── lesser_elemental_ghost.tres │ │ │ ├── lesser_flamy_defender.tres │ │ │ ├── lesser_ice_defender.tres │ │ │ ├── lesser_iron_defender.tres │ │ │ ├── lesser_natural_defender.tres │ │ │ ├── lesser_priest.tres │ │ │ ├── lesser_skeletal_mage.tres │ │ │ ├── lesser_storm_defender.tres │ │ │ ├── lesser_wolves_den.tres │ │ │ ├── library_of_alexandria.tres │ │ │ ├── lich_king.tres │ │ │ ├── lightning_eye.tres │ │ │ ├── lightning_generator.tres │ │ │ ├── lightning_totem.tres │ │ │ ├── little_phoenix.tres │ │ │ ├── living_volcano.tres │ │ │ ├── lunar_emitter.tres │ │ │ ├── lunar_sentinel.tres │ │ │ ├── magic_battery.tres │ │ │ ├── magic_mushroom.tres │ │ │ ├── magna_warrior.tres │ │ │ ├── mana-touched_drake.tres │ │ │ ├── marine.tres │ │ │ ├── meteor_totem.tres │ │ │ ├── militia_watchtower.tres │ │ │ ├── miner.tres │ │ │ ├── minor_magic_ruin.tres │ │ │ ├── mister_fireflies.tres │ │ │ ├── monolith_of_chaos.tres │ │ │ ├── morphling.tres │ │ │ ├── mossy_acid_sprayer.tres │ │ │ ├── mud_golem.tres │ │ │ ├── nature_sprites.tres │ │ │ ├── necromantic_altar.tres │ │ │ ├── nortrom_the_silencer.tres │ │ │ ├── nuclear_missile_launcher.tres │ │ │ ├── obelisk_of_fortuity.tres │ │ │ ├── owl_of_wisdom.tres │ │ │ ├── particle_accelerator.tres │ │ │ ├── phantom.tres │ │ │ ├── plagued_crypt.tres │ │ │ ├── planar_gate.tres │ │ │ ├── poison_battery.tres │ │ │ ├── polar_bear_cub.tres │ │ │ ├── portal_to_swine_purgatory.tres │ │ │ ├── prince_of_lightning.tres │ │ │ ├── princess_of_light.tres │ │ │ ├── razorboar_thornweaver.tres │ │ │ ├── red_ball_lightning.tres │ │ │ ├── regenerating_well.tres │ │ │ ├── rooted_chasm.tres │ │ │ ├── rotted_flashing_grave.tres │ │ │ ├── rowing_boat.tres │ │ │ ├── ruined_monolith.tres │ │ │ ├── ruined_storm_cap.tres │ │ │ ├── ruined_sun_pedestal.tres │ │ │ ├── ruined_wind_tower.tres │ │ │ ├── rundown_iron_sentry.tres │ │ │ ├── sacred_altar.tres │ │ │ ├── sacrificial_lamb.tres │ │ │ ├── safirons_cold_grave.tres │ │ │ ├── scales.tres │ │ │ ├── sea_turtle.tres │ │ │ ├── servant_of_the_twin_flames.tres │ │ │ ├── sewer_connection.tres │ │ │ ├── shadow.tres │ │ │ ├── shaman.tres │ │ │ ├── shard_of_souls.tres │ │ │ ├── silver_knight.tres │ │ │ ├── skink.tres │ │ │ ├── small_brazier.tres │ │ │ ├── small_bug_nest.tres │ │ │ ├── small_cactus.tres │ │ │ ├── small_fire_sprayer.tres │ │ │ ├── small_frost_fire.tres │ │ │ ├── small_frozen_mushroom.tres │ │ │ ├── small_ice_mine.tres │ │ │ ├── small_light.tres │ │ │ ├── small_pocket_rocket.tres │ │ │ ├── small_ray_blaster.tres │ │ │ ├── small_serpent_ward.tres │ │ │ ├── small_torch.tres │ │ │ ├── sniper.tres │ │ │ ├── snowy_pebble.tres │ │ │ ├── solar_collector.tres │ │ │ ├── solar_emitter.tres │ │ │ ├── sorceress.tres │ │ │ ├── soul_vault.tres │ │ │ ├── soulflame_device.tres │ │ │ ├── spell_collector.tres │ │ │ ├── spider_queen.tres │ │ │ ├── star_gazer.tres │ │ │ ├── storm_battery.tres │ │ │ ├── storm_coil.tres │ │ │ ├── storm_focus.tres │ │ │ ├── stormy_dog.tres │ │ │ ├── sun_crusader.tres │ │ │ ├── taita_the_hermit.tres │ │ │ ├── teacher.tres │ │ │ ├── tentacle_spawn.tres │ │ │ ├── the_conduit.tres │ │ │ ├── the_council_of_demons.tres │ │ │ ├── the_fire_lord.tres │ │ │ ├── the_frozen_wyrm.tres │ │ │ ├── the_furnace.tres │ │ │ ├── the_omnislasher.tres │ │ │ ├── the_steam_engine.tres │ │ │ ├── thief_apprentice.tres │ │ │ ├── tidewater_stream.tres │ │ │ ├── time_manipulator.tres │ │ │ ├── timevault.tres │ │ │ ├── tiny_shrub.tres │ │ │ ├── tiny_storm_lantern.tres │ │ │ ├── tombstone.tres │ │ │ ├── trash_heap.tres │ │ │ ├── tree_stump.tres │ │ │ ├── tundra_stalker.tres │ │ │ ├── undisturbed_crypt.tres │ │ │ ├── valor.tres │ │ │ ├── village_witch.tres │ │ │ ├── void_drake.tres │ │ │ ├── vulshok_the_berserker.tres │ │ │ ├── warrior_of_light.tres │ │ │ ├── wild_warbeast.tres │ │ │ ├── witch_doctor.tres │ │ │ ├── wooden_trap.tres │ │ │ ├── xeno_research_facility.tres │ │ │ ├── ymir.tres │ │ │ ├── young_northern_troll.tres │ │ │ ├── zealot.tres │ │ │ └── zeus.tres │ │ ├── tower_variations/ │ │ │ ├── ash_geyser_blue.tres │ │ │ ├── ash_geyser_green.tres │ │ │ ├── ash_geyser_purple.tres │ │ │ ├── bonk_the_living_mountain_small.tres │ │ │ ├── forest_troll_small.tres │ │ │ ├── meteor_totem_blue.tres │ │ │ ├── meteor_totem_purple.tres │ │ │ ├── mossy_acid_sprayer_gray.tres │ │ │ ├── mossy_acid_sprayer_red.tres │ │ │ ├── undisturbed_crypt_gold.tres │ │ │ └── vulshok_the_berserker_blue.tres │ │ ├── trinkets/ │ │ │ ├── claw_01.tres │ │ │ ├── claw_02.tres │ │ │ ├── claw_03.tres │ │ │ ├── key_01.tres │ │ │ ├── key_02.tres │ │ │ ├── key_03.tres │ │ │ ├── trinket_01.tres │ │ │ ├── trinket_02.tres │ │ │ ├── trinket_03.tres │ │ │ ├── trinket_04.tres │ │ │ ├── trinket_05.tres │ │ │ ├── trinket_06.tres │ │ │ ├── trinket_07.tres │ │ │ ├── trinket_08.tres │ │ │ ├── trinket_09.tres │ │ │ └── trinket_10.tres │ │ ├── undead/ │ │ │ ├── demon_emblem.tres │ │ │ ├── monster_hand.tres │ │ │ ├── skull_01.tres │ │ │ ├── skull_02.tres │ │ │ ├── skull_03.tres │ │ │ ├── skull_04.tres │ │ │ ├── skull_05.tres │ │ │ ├── skull_06.tres │ │ │ ├── skull_doll.tres │ │ │ ├── skull_phazing.tres │ │ │ ├── skull_wand_01.tres │ │ │ ├── skull_wand_02.tres │ │ │ ├── skull_wand_03.tres │ │ │ └── skull_wand_04.tres │ │ └── weapons_misc/ │ │ ├── axe_01.tres │ │ ├── axe_02.tres │ │ ├── barbed_spike.tres │ │ ├── glaive_01.tres │ │ ├── glaive_02.tres │ │ ├── glaive_03.tres │ │ ├── mining_pick_01.tres │ │ ├── mining_pick_02.tres │ │ ├── trident_01.tres │ │ └── trident_02.tres │ ├── misc/ │ │ ├── barrel.tres │ │ └── pulsing_dot.tres │ ├── shaders/ │ │ ├── foggy_camera.gdshader │ │ ├── glowing_outline.gdshader │ │ ├── glowing_outline.material │ │ ├── glowing_outline_2.gdshader │ │ └── saturation_burn.gdshader │ ├── theme/ │ │ ├── common/ │ │ │ ├── rect_container_center_l.tres │ │ │ ├── rect_container_l.tres │ │ │ ├── rect_container_m.tres │ │ │ ├── rect_container_s.tres │ │ │ └── tab_container_m.tres │ │ ├── element_towers_info/ │ │ │ └── empty_button_style_box.tres │ │ ├── health_bar_background.tres │ │ ├── health_bar_fill.tres │ │ ├── item_button_hover.tres │ │ ├── item_button_normal.tres │ │ ├── item_button_pressed.tres │ │ ├── mission_complete_indicator.tres │ │ ├── player_resource_display/ │ │ │ ├── circle_container_l.tres │ │ │ ├── rect_container_center_l.tres │ │ │ ├── rect_container_left_l.tres │ │ │ ├── rect_container_right_l.tres │ │ │ ├── rect_container_top_menu.tres │ │ │ ├── resource_status_panel.tres │ │ │ └── stash_panel_background.tres │ │ ├── selected_unit_info/ │ │ │ ├── mana_bar_fill.tres │ │ │ ├── panel_container.tres │ │ │ └── progress_bar_background.tres │ │ ├── tower_actions/ │ │ │ ├── button_disabled.tres │ │ │ ├── button_normal.tres │ │ │ └── button_pressed.tres │ │ ├── unit_button/ │ │ │ ├── button_disabled.tres │ │ │ ├── button_hover.tres │ │ │ ├── button_normal.tres │ │ │ ├── common_rarity_panel_container.tres │ │ │ ├── passive_ability_button.tres │ │ │ ├── rare_rarity_panel_container.tres │ │ │ ├── small_button_disabled.tres │ │ │ ├── small_button_hover.tres │ │ │ ├── small_button_normal.tres │ │ │ ├── uncommon_rarity_panel_container.tres │ │ │ └── unique_rarity_panel_container.tres │ │ ├── unit_button_container/ │ │ │ └── counter_label_normal.tres │ │ ├── unit_menu/ │ │ │ ├── button_disalbed.tres │ │ │ ├── button_hover.tres │ │ │ ├── button_normal.tres │ │ │ ├── exp_bar_background.tres │ │ │ ├── exp_bar_fill.tres │ │ │ ├── health_bar_background.tres │ │ │ ├── health_bar_fill.tres │ │ │ ├── mana_bar_background.tres │ │ │ ├── mana_bar_fill.tres │ │ │ ├── title_button_normal.tres │ │ │ ├── unit_level_label_normal.tres │ │ │ ├── unit_menu_panel.tres │ │ │ └── unit_name_label_normal.tres │ │ └── wc3_theme.tres │ ├── tilesets/ │ │ ├── buildable_area_tiles.tres │ │ └── decorations.tres │ └── ui_textures/ │ ├── buff_group_both.tres │ ├── buff_group_incoming.tres │ ├── buff_group_none.tres │ ├── buff_group_outgoing.tres │ ├── button_container_hover.tres │ ├── common_background.tres │ ├── common_unit_button.tres │ ├── common_unit_button_hover.tres │ ├── info_icon.tres │ ├── menu_option_round_button.tres │ ├── menu_option_round_button_hover.tres │ ├── portal_lives_round_bg.tres │ ├── rare_background.tres │ ├── rare_unit_button.tres │ ├── rare_unit_button_hover.tres │ ├── rect_container_center_l.tres │ ├── rect_container_l.tres │ ├── rect_container_left_l.tres │ ├── rect_container_m.tres │ ├── rect_container_right_l.tres │ ├── rect_container_s.tres │ ├── rect_container_s_hover.tres │ ├── speed_fast.tres │ ├── speed_fastest.tres │ ├── speed_normal.tres │ ├── stash_panel_background.tres │ ├── uncommon_background.tres │ ├── uncommon_unit_button.tres │ ├── uncommon_unit_button_hover.tres │ ├── unique_background.tres │ ├── unique_unit_button.tres │ ├── unique_unit_button_hover.tres │ ├── unit_button_disabled.tres │ └── upgrade_icon.tres ├── src/ │ ├── README.md │ ├── actions/ │ │ ├── action.gd │ │ ├── action_autocast.gd │ │ ├── action_autofill.gd │ │ ├── action_build_tower.gd │ │ ├── action_change_buffgroup.gd │ │ ├── action_chat.gd │ │ ├── action_consume_item.gd │ │ ├── action_drop_item.gd │ │ ├── action_focus_target.gd │ │ ├── action_idle.gd │ │ ├── action_move_item.gd │ │ ├── action_research_element.gd │ │ ├── action_roll_towers.gd │ │ ├── action_select_builder.gd │ │ ├── action_select_unit.gd │ │ ├── action_select_wisdom_upgrades.gd │ │ ├── action_sell_tower.gd │ │ ├── action_sort_item_stash.gd │ │ ├── action_start_next_wave.gd │ │ ├── action_swap_items.gd │ │ ├── action_toggle_autocast.gd │ │ ├── action_transform_tower.gd │ │ ├── action_transmute.gd │ │ └── action_upgrade_tower.gd │ ├── buffs/ │ │ ├── aura.gd │ │ ├── aura.tscn │ │ ├── buff.gd │ │ ├── buff_range_area.gd │ │ ├── buff_range_area.tscn │ │ ├── buff_type.gd │ │ ├── instances/ │ │ │ ├── cb_silence.gd │ │ │ └── cb_stun.gd │ │ ├── modification.gd │ │ ├── modifier.gd │ │ └── target_type.gd │ ├── builders/ │ │ ├── builder.gd │ │ └── instances/ │ │ ├── builder_adventurer.gd │ │ ├── builder_antagonizer.gd │ │ ├── builder_arch_sorceress.gd │ │ ├── builder_backpacker.gd │ │ ├── builder_barbarian_warlord.gd │ │ ├── builder_benevolent_witch.gd │ │ ├── builder_blademaster.gd │ │ ├── builder_elementalist.gd │ │ ├── builder_farseer.gd │ │ ├── builder_feeble_fender.gd │ │ ├── builder_goblin_alchemist.gd │ │ ├── builder_iron_maiden.gd │ │ ├── builder_master_of_encyclopedic_wisdom.gd │ │ ├── builder_maverick.gd │ │ ├── builder_naga_time_twister.gd │ │ ├── builder_none.gd │ │ ├── builder_panda_monk.gd │ │ ├── builder_queen.gd │ │ ├── builder_realist.gd │ │ ├── builder_royal_assassin.gd │ │ ├── builder_spirit_warden.gd │ │ └── builder_veteran_pilot.gd │ ├── creeps/ │ │ ├── creep.gd │ │ ├── creep_blood_pool.gd │ │ ├── creep_blood_pool.tscn │ │ ├── creep_corpse.gd │ │ ├── creep_corpse.tscn │ │ ├── creep_spawner.gd │ │ ├── creep_spawner.tscn │ │ ├── creep_sprite.gd │ │ ├── health_bar.gd │ │ ├── instances/ │ │ │ ├── challenge/ │ │ │ │ ├── challenge_boss_creep.tscn │ │ │ │ └── challenge_mass_creep.tscn │ │ │ ├── creep.tscn │ │ │ └── orc/ │ │ │ ├── orc_air_creep.tscn │ │ │ ├── orc_boss_creep.tscn │ │ │ ├── orc_champion_creep.tscn │ │ │ ├── orc_mass_creep.tscn │ │ │ └── orc_normal_creep.tscn │ │ ├── packed_metadata.gd │ │ ├── special_buffs/ │ │ │ ├── creep_armored.gd │ │ │ ├── creep_broody.gd │ │ │ ├── creep_dart.gd │ │ │ ├── creep_ethereal.gd │ │ │ ├── creep_evasion.gd │ │ │ ├── creep_evolving.gd │ │ │ ├── creep_fireball.gd │ │ │ ├── creep_flock.gd │ │ │ ├── creep_ghost.gd │ │ │ ├── creep_gravid.gd │ │ │ ├── creep_greater_speed.gd │ │ │ ├── creep_greater_spell_resistance.gd │ │ │ ├── creep_heavy_armored.gd │ │ │ ├── creep_invisible.gd │ │ │ ├── creep_magic_immunity.gd │ │ │ ├── creep_mana_drain_aura.gd │ │ │ ├── creep_manashield+.gd │ │ │ ├── creep_manashield.gd │ │ │ ├── creep_meaty.gd │ │ │ ├── creep_mechanical.gd │ │ │ ├── creep_necromancer.gd │ │ │ ├── creep_protector.gd │ │ │ ├── creep_purge_revenge.gd │ │ │ ├── creep_regeneration.gd │ │ │ ├── creep_relic_raider.gd │ │ │ ├── creep_rich.gd │ │ │ ├── creep_second_chance.gd │ │ │ ├── creep_semi-mechanical.gd │ │ │ ├── creep_slow.gd │ │ │ ├── creep_slow_aura.gd │ │ │ ├── creep_speed.gd │ │ │ ├── creep_spell_resistance.gd │ │ │ ├── creep_spellbinder.gd │ │ │ ├── creep_strong.gd │ │ │ ├── creep_stun_revenge.gd │ │ │ ├── creep_ultra_wisdom.gd │ │ │ ├── creep_unlucky.gd │ │ │ ├── creep_xtreme_armor.gd │ │ │ ├── creep_xtreme_evasion.gd │ │ │ ├── creep_xtreme_regeneration.gd │ │ │ └── creep_xtreme_speed.gd │ │ ├── special_icons/ │ │ │ ├── CreepArmoredSpecial.tscn │ │ │ ├── CreepBroodySpecial.tscn │ │ │ ├── CreepDartSpecial.tscn │ │ │ ├── CreepEtherealSpecial.tscn │ │ │ ├── CreepEvasionSpecial.tscn │ │ │ ├── CreepEvolvingSpecial.tscn │ │ │ ├── CreepFireballSpecial.tscn │ │ │ ├── CreepFlockSpecial.tscn │ │ │ ├── CreepGhostSpecial.tscn │ │ │ ├── CreepGravidSpecial.tscn │ │ │ ├── CreepGreaterSpeedSpecial.tscn │ │ │ ├── CreepGreaterSpellResistanceSpecial.tscn │ │ │ ├── CreepHeavyArmoredSpecial.tscn │ │ │ ├── CreepInvisibleSpecial.tscn │ │ │ ├── CreepMagicImmunitySpecial.tscn │ │ │ ├── CreepManaDrainAuraSpecial.tscn │ │ │ ├── CreepManaShieldPlusSpecial.tscn │ │ │ ├── CreepManaShieldSpecial.tscn │ │ │ ├── CreepMeatySpecial.tscn │ │ │ ├── CreepMechanicalSpecial.tscn │ │ │ ├── CreepNecromancerSpecial.tscn │ │ │ ├── CreepProtectorSpecial.tscn │ │ │ ├── CreepPurgeRevengeSpecial.tscn │ │ │ ├── CreepRegenerationSpecial.tscn │ │ │ ├── CreepRelicRaiderSpecial.tscn │ │ │ ├── CreepRichSpecial.tscn │ │ │ ├── CreepSecondChanceSpecial.tscn │ │ │ ├── CreepSemiMechanicalSpecial.tscn │ │ │ ├── CreepSlowAuraSpecial.tscn │ │ │ ├── CreepSlowSpecial.tscn │ │ │ ├── CreepSpeedSpecial.tscn │ │ │ ├── CreepSpellResistanceSpecial.tscn │ │ │ ├── CreepSpellbinderSpecial.tscn │ │ │ ├── CreepStrongSpecial.tscn │ │ │ ├── CreepStunRevengeSpecial.tscn │ │ │ ├── CreepUltraWisdomSpecial.tscn │ │ │ ├── CreepUnluckySpecial.tscn │ │ │ ├── CreepXtremeArmorSpecial.tscn │ │ │ ├── CreepXtremeEvasionSpecial.tscn │ │ │ ├── CreepXtremeRegenerationSpecial.tscn │ │ │ └── CreepXtremeSpeedSpecial.tscn │ │ ├── wave.gd │ │ ├── wave_path.gd │ │ ├── wave_spawner.gd │ │ └── wave_spawner.tscn │ ├── effects/ │ │ ├── ancient_protector_missile.tscn │ │ ├── animated_dead_target.tscn │ │ ├── apply_potion.tscn │ │ ├── arcane_tower_attack.tscn │ │ ├── arcane_tower_attack_flat.tscn │ │ ├── avatar_caster.tscn │ │ ├── banshee_missile.tscn │ │ ├── barrel.tscn │ │ ├── blink_target.tscn │ │ ├── blood_splatter.tscn │ │ ├── build_tower.tscn │ │ ├── charm_target.tscn │ │ ├── chimaera_acid.tscn │ │ ├── cloud_of_fog_cycle.tscn │ │ ├── cloud_of_fog_small.tscn │ │ ├── cripple_target.tscn │ │ ├── crushing_wave.tscn │ │ ├── crypt_fiend_eggsack.tscn │ │ ├── cyclone_target.tscn │ │ ├── death_and_decay.tscn │ │ ├── death_coil.tscn │ │ ├── death_explode.tscn │ │ ├── devour.tscn │ │ ├── dispel_magic_target.tscn │ │ ├── doom_death.tscn │ │ ├── effects_container.gd │ │ ├── faerie_dragon_missile.tscn │ │ ├── firelord_death_explode.tscn │ │ ├── flame_strike_embers.tscn │ │ ├── flower_aura.tscn │ │ ├── frag_boom_spawn.tscn │ │ ├── freezing_breath.tscn │ │ ├── freezing_breath_purple.tscn │ │ ├── frost_armor_damage.tscn │ │ ├── frost_armor_damage_purple.tscn │ │ ├── frost_bolt_missile.tscn │ │ ├── glaive.tscn │ │ ├── gold_credit.tscn │ │ ├── healing_wave_target.tscn │ │ ├── holy_bolt.tscn │ │ ├── holy_bolt_green.tscn │ │ ├── howl_caster.tscn │ │ ├── immolation_damage.tscn │ │ ├── impale_hit_target.tscn │ │ ├── impale_target_dust.tscn │ │ ├── incinerate.tscn │ │ ├── interpolated_sprite.gd │ │ ├── keeper_grove_missile.tscn │ │ ├── level_up.tscn │ │ ├── mana_burn_target.tscn │ │ ├── mana_shield_cycle.tscn │ │ ├── mass_teleport_caster.tscn │ │ ├── mass_teleport_target.tscn │ │ ├── monsoon_bolt.tscn │ │ ├── moonwell_target.tscn │ │ ├── mortar_missile.tscn │ │ ├── naga_death.tscn │ │ ├── ne_cancel_death.tscn │ │ ├── ne_death.tscn │ │ ├── placeholder.tscn │ │ ├── polymorph_target.tscn │ │ ├── projectile_explosion_astral.tscn │ │ ├── projectile_explosion_darkness.tscn │ │ ├── projectile_explosion_fire.tscn │ │ ├── projectile_explosion_ice.tscn │ │ ├── projectile_explosion_iron.tscn │ │ ├── projectile_explosion_nature.tscn │ │ ├── projectile_explosion_storm.tscn │ │ ├── purge_buff_target.tscn │ │ ├── quillspray.tscn │ │ ├── raise_skeleton.tscn │ │ ├── replenish_mana.tscn │ │ ├── revive_human.tscn │ │ ├── roar.tscn │ │ ├── roots.tscn │ │ ├── shackle.tscn │ │ ├── shockwave_missile.tscn │ │ ├── silence_area.tscn │ │ ├── small_flame_spawn.tscn │ │ ├── spell_aiil.tscn │ │ ├── spell_ailb.tscn │ │ ├── spell_aima.tscn │ │ ├── spell_aire.tscn │ │ ├── spell_aire_flat.tscn │ │ ├── spell_aiso.tscn │ │ ├── spell_alim.tscn │ │ ├── spell_breaker_target.tscn │ │ ├── spirit_link_target.tscn │ │ ├── stampede_missile_death.tscn │ │ ├── starfall_target.tscn │ │ ├── stun_visual.tscn │ │ ├── target_arrow.gd │ │ ├── target_arrow.tscn │ │ ├── thunder_clap.tscn │ │ ├── undead_dissipate.tscn │ │ ├── upgrade_tower.tscn │ │ ├── vampiric_aura.tscn │ │ ├── voodoo_aura.tscn │ │ ├── warstomp_caster.tscn │ │ ├── wind_shear.tscn │ │ ├── wisp_explode.tscn │ │ ├── witch_doctor_ward.tscn │ │ ├── ziggurat_frost_missile.tscn │ │ └── zombify_target.tscn │ ├── enums/ │ │ ├── README.md │ │ ├── armor_type.gd │ │ ├── attack_type.gd │ │ ├── buff_group_mode.gd │ │ ├── builder_tier.gd │ │ ├── creep_category.gd │ │ ├── creep_size.gd │ │ ├── difficulty.gd │ │ ├── display_mode.gd │ │ ├── element.gd │ │ ├── game_mode.gd │ │ ├── item_type.gd │ │ ├── language.gd │ │ ├── modification_type.gd │ │ ├── player_mode.gd │ │ ├── rarity.gd │ │ └── team_mode.gd │ ├── game_scene/ │ │ ├── build_space.gd │ │ ├── build_tower.gd │ │ ├── camera_controller.gd │ │ ├── chat_commands.gd │ │ ├── combat_log_storage.gd │ │ ├── game_client.gd │ │ ├── game_client.tscn │ │ ├── game_host.gd │ │ ├── game_host.tscn │ │ ├── game_scene.gd │ │ ├── game_scene.tscn │ │ ├── game_time.gd │ │ ├── horadric_cube.gd │ │ ├── manual_timer.gd │ │ ├── mouse_state.gd │ │ ├── move_item.gd │ │ ├── select_point_for_cast.gd │ │ ├── select_target_for_cast.gd │ │ ├── select_unit.gd │ │ ├── team_container.gd │ │ └── tutorial_controller.gd │ ├── items/ │ │ ├── README.md │ │ ├── item.gd │ │ ├── item_behavior.gd │ │ ├── item_behaviors/ │ │ │ ├── aqueous_vapor.gd │ │ │ ├── arcane_book_of_power.gd │ │ │ ├── arcane_oil_of_lore.gd │ │ │ ├── arcane_script.gd │ │ │ ├── arms_dealer.gd │ │ │ ├── artifact_of_skadi.gd │ │ │ ├── backpack.gd │ │ │ ├── ball_lightning.gd │ │ │ ├── bartucs_spirit.gd │ │ │ ├── basics_of_calculus.gd │ │ │ ├── bhaals_essence.gd │ │ │ ├── blaster_staff.gd │ │ │ ├── bloodthirsty_wheel_of_fortune.gd │ │ │ ├── bloody_key.gd │ │ │ ├── bones_of_essence.gd │ │ │ ├── bonks_face.gd │ │ │ ├── book_of_force.gd │ │ │ ├── book_of_knowledge.gd │ │ │ ├── brimstone_helmet.gd │ │ │ ├── chameleon_glaive.gd │ │ │ ├── chameleons_soul.gd │ │ │ ├── charged_disk.gd │ │ │ ├── chrono_jumper.gd │ │ │ ├── circle_of_power.gd │ │ │ ├── commander.gd │ │ │ ├── consumable_chicken.gd │ │ │ ├── consumable_hobbit.gd │ │ │ ├── consumable_piggy.gd │ │ │ ├── consumable_plant.gd │ │ │ ├── crescent_stone.gd │ │ │ ├── crit_blade.gd │ │ │ ├── cruel_torch.gd │ │ │ ├── currency_converter.gd │ │ │ ├── cursed_claw.gd │ │ │ ├── dagger_of_bane.gd │ │ │ ├── dark_matter_trident.gd │ │ │ ├── deep_shadows.gd │ │ │ ├── distorted_idol.gd │ │ │ ├── divine_book_of_omnipotence.gd │ │ │ ├── dooms_ensign.gd │ │ │ ├── elunes_bow.gd │ │ │ ├── empty_item_behavior.gd │ │ │ ├── enchanted_knives.gd │ │ │ ├── essence_of_rot.gd │ │ │ ├── even_more_magical_hammer.gd │ │ │ ├── excalibur.gd │ │ │ ├── eye_of_true_sight.gd │ │ │ ├── faithful_staff.gd │ │ │ ├── fist_of_doom.gd │ │ │ ├── flag_of_the_allegiance.gd │ │ │ ├── forcefield_generator.gd │ │ │ ├── fragmentation_round.gd │ │ │ ├── frog_pipe.gd │ │ │ ├── glaive_of_supreme_follow_up.gd │ │ │ ├── golden_decoration.gd │ │ │ ├── golden_trident.gd │ │ │ ├── granite_hammer.gd │ │ │ ├── grounding_gloves.gd │ │ │ ├── haunted_hand.gd │ │ │ ├── helm_of_insanity.gd │ │ │ ├── hippogryph_egg.gd │ │ │ ├── holy_hand_grenade.gd │ │ │ ├── item_template_advanced.gd │ │ │ ├── jah_rakals_fury.gd │ │ │ ├── jewels_of_the_moon.gd │ │ │ ├── jungle_stalkers_doll.gd │ │ │ ├── libram_of_grace.gd │ │ │ ├── lich_mask.gd │ │ │ ├── liquid_gold.gd │ │ │ ├── lucky_gem.gd │ │ │ ├── lunar_essence.gd │ │ │ ├── magic_conductor.gd │ │ │ ├── magic_gloves.gd │ │ │ ├── magic_hammer.gd │ │ │ ├── magic_link.gd │ │ │ ├── magnetic_field.gd │ │ │ ├── mana_stone.gd │ │ │ ├── medallion_of_opulence.gd │ │ │ ├── mefis_rocket.gd │ │ │ ├── mighty_trees_acorns.gd │ │ │ ├── mindleecher.gd │ │ │ ├── mine_cart.gd │ │ │ ├── mini_forest_troll.gd │ │ │ ├── mining_tools.gd │ │ │ ├── mystical_shell.gd │ │ │ ├── nerminds_eye.gd │ │ │ ├── never-ending_keg.gd │ │ │ ├── oil_of_lore.gd │ │ │ ├── old_hunter.gd │ │ │ ├── optimists_preserved_face.gd │ │ │ ├── orb_of_souls.gd │ │ │ ├── overcharge_shot.gd │ │ │ ├── pendant_of_mana_supremacy.gd │ │ │ ├── pendant_of_promptness.gd │ │ │ ├── phase_gloves.gd │ │ │ ├── plain_staff.gd │ │ │ ├── pocket_emporium.gd │ │ │ ├── polarisator.gd │ │ │ ├── portable_tombstone.gd │ │ │ ├── priest_figurine.gd │ │ │ ├── purifying_gloves.gd │ │ │ ├── ritual_talisman.gd │ │ │ ├── scroll_of_piercing_magic.gd │ │ │ ├── scroll_of_speed.gd │ │ │ ├── scroll_of_strength.gd │ │ │ ├── seekers_arcane_oil.gd │ │ │ ├── seekers_oil.gd │ │ │ ├── share_knowledge.gd │ │ │ ├── shining_rock.gd │ │ │ ├── shrapnel_ammunition.gd │ │ │ ├── sign_of_energy_infusion.gd │ │ │ ├── silver_armor.gd │ │ │ ├── sleeve_of_rage.gd │ │ │ ├── soul_collectors_cloak.gd │ │ │ ├── soul_collectors_scythe.gd │ │ │ ├── soul_extractor.gd │ │ │ ├── spear_of_loki.gd │ │ │ ├── speed_demons_reward.gd │ │ │ ├── spellbook_of_item_mastery.gd │ │ │ ├── spider_broach.gd │ │ │ ├── spiderling.gd │ │ │ ├── staff_of_essence.gd │ │ │ ├── staff_of_the_wild_equus.gd │ │ │ ├── stasis_trap.gd │ │ │ ├── strange_item.gd │ │ │ ├── stunner.gd │ │ │ ├── sword_of_decay.gd │ │ │ ├── sword_of_reckoning.gd │ │ │ ├── the_divine_wings_of_tragedy.gd │ │ │ ├── toy_boy.gd │ │ │ ├── unstable_current.gd │ │ │ ├── unyielding_maul.gd │ │ │ ├── vampiric_skull.gd │ │ │ ├── wand_of_mana_zap.gd │ │ │ ├── wanted_list.gd │ │ │ ├── war_drum.gd │ │ │ ├── wise_mans_cooking_recipe.gd │ │ │ ├── wizards_soul.gd │ │ │ ├── wooden_leg.gd │ │ │ ├── workbench.gd │ │ │ └── writers_knowledge.gd │ │ ├── item_container.gd │ │ ├── item_drop.gd │ │ ├── item_drop.tscn │ │ └── tower_item_container.gd │ ├── map/ │ │ ├── buildable_area.gd │ │ ├── buildable_area.tscn │ │ ├── map.gd │ │ ├── map_big.tscn │ │ └── map_small.tscn │ ├── missions/ │ │ ├── instances/ │ │ │ ├── mission_120_in_60min.gd │ │ │ ├── mission_240_in_120min.gd │ │ │ ├── mission_35_food_or_less.gd │ │ │ ├── mission_empty.gd │ │ │ ├── mission_no_items.gd │ │ │ ├── mission_no_oils.gd │ │ │ ├── mission_only_astral_towers.gd │ │ │ ├── mission_only_common_uncommon.gd │ │ │ ├── mission_only_darkness_towers.gd │ │ │ ├── mission_only_fire_towers.gd │ │ │ ├── mission_only_ice_towers.gd │ │ │ ├── mission_only_iron_towers.gd │ │ │ ├── mission_only_nature_towers.gd │ │ │ └── mission_only_storm_towers.gd │ │ ├── mission.gd │ │ ├── mission_manager.gd │ │ └── mission_manager.tscn │ ├── player/ │ │ ├── auto_oil.gd │ │ ├── camera_origin.gd │ │ ├── camera_origin.tscn │ │ ├── player.gd │ │ ├── player.tscn │ │ ├── team.gd │ │ ├── team.tscn │ │ └── tower_stash.gd │ ├── projectiles/ │ │ ├── projectile.gd │ │ ├── projectile.tscn │ │ ├── projectile_type.gd │ │ └── projectile_visuals/ │ │ ├── ball_lightning_projectile.tscn │ │ ├── default_projectile.gd │ │ ├── default_projectile.tscn │ │ ├── energy_ball.tscn │ │ ├── flying_pork.tscn │ │ ├── omnislasher_mirror_image.tscn │ │ └── quillspray_projectile.tscn │ ├── singletons/ │ │ ├── README.md │ │ ├── button_sounds.gd │ │ ├── combat_log.gd │ │ ├── config.gd │ │ ├── constants.gd │ │ ├── effect.gd │ │ ├── elapsed_timer.gd │ │ ├── event_bus.gd │ │ ├── experience.gd │ │ ├── experience_password.gd │ │ ├── globals.gd │ │ ├── group_manager.gd │ │ ├── item_drop_calc.gd │ │ ├── messages.gd │ │ ├── mission_status.gd │ │ ├── mission_tracking.gd │ │ ├── nakama_connection.gd │ │ ├── player_experience.gd │ │ ├── player_manager.gd │ │ ├── preloads.gd │ │ ├── properties/ │ │ │ ├── README.md │ │ │ ├── ability_properties.gd │ │ │ ├── aura_properties.gd │ │ │ ├── autocast_properties.gd │ │ │ ├── builder_properties.gd │ │ │ ├── item_properties.gd │ │ │ ├── mission_properties.gd │ │ │ ├── recipe_properties.gd │ │ │ ├── tower_properties.gd │ │ │ ├── tutorial_properties.gd │ │ │ ├── wave_special_properties.gd │ │ │ └── wisdom_upgrade_properties.gd │ │ ├── rich_texts.gd │ │ ├── sanitize_text.gd │ │ ├── secrets.gd │ │ ├── settings.gd │ │ ├── sfx.gd │ │ ├── sfx_paths.gd │ │ ├── tower_distribution.gd │ │ ├── tower_sprites.gd │ │ ├── unit_icons.gd │ │ ├── utils.gd │ │ ├── vector_utils.gd │ │ └── wave_special.gd │ ├── spells/ │ │ ├── spell_blizzard.gd │ │ ├── spell_blizzard.tscn │ │ ├── spell_chain_lightning.gd │ │ ├── spell_chain_lightning.tscn │ │ ├── spell_dummy.gd │ │ ├── spell_dummy.tscn │ │ ├── spell_forked_lightning.gd │ │ ├── spell_forked_lightning.tscn │ │ ├── spell_swarm.gd │ │ ├── spell_swarm.tscn │ │ └── spell_type.gd │ ├── sprites/ │ │ ├── lightning_animation.tscn │ │ └── pulsing_dot.tscn │ ├── tests/ │ │ ├── README.md │ │ ├── playtest_bot.gd │ │ ├── test_horadric_tool.gd │ │ ├── test_item_drop_chances.gd │ │ ├── test_items_tool.gd │ │ ├── test_tool.gd │ │ ├── test_tower_sprite_size.gd │ │ └── test_towers_tool.gd │ ├── towers/ │ │ ├── README.md │ │ ├── autocast.gd │ │ ├── autocast.tscn │ │ ├── event.gd │ │ ├── multiboard_values.gd │ │ ├── range_data.gd │ │ ├── range_indicator.gd │ │ ├── range_indicator.tscn │ │ ├── tower.gd │ │ ├── tower.tscn │ │ ├── tower_behavior.gd │ │ ├── tower_behavior_template.gd │ │ ├── tower_behaviors/ │ │ │ ├── afflicted_obelisk.gd │ │ │ ├── ancient_energy_converter.gd │ │ │ ├── annoyed_tree.gd │ │ │ ├── arcane_storm.gd │ │ │ ├── area_roaster.gd │ │ │ ├── ash_geyser.gd │ │ │ ├── astral_lantern.gd │ │ │ ├── astral_rift.gd │ │ │ ├── baby_tuskin.gd │ │ │ ├── ball_lightning_accelerator.gd │ │ │ ├── basic_knowledge.gd │ │ │ ├── black_dragon_roost.gd │ │ │ ├── black_rock_totem.gd │ │ │ ├── bomb_turret.gd │ │ │ ├── bone_shrine.gd │ │ │ ├── bonk_the_living_mountain.gd │ │ │ ├── broken_cage.gd │ │ │ ├── broken_circle_of_wind.gd │ │ │ ├── broken_fire_pit.gd │ │ │ ├── broken_lightning_rod.gd │ │ │ ├── bronze_dragon_roost.gd │ │ │ ├── buried_soul.gd │ │ │ ├── burning_watchtower.gd │ │ │ ├── burrow.gd │ │ │ ├── caged_fire.gd │ │ │ ├── cenarion.gd │ │ │ ├── chaining_storm.gd │ │ │ ├── chaos_warlock.gd │ │ │ ├── charged_obelisk.gd │ │ │ ├── chilled_spire.gd │ │ │ ├── cloud_warrior.gd │ │ │ ├── cloudy_temple_of_absorption.gd │ │ │ ├── coconut_sapling.gd │ │ │ ├── coin_machine.gd │ │ │ ├── cold_obelisk.gd │ │ │ ├── cold_troll.gd │ │ │ ├── contraption.gd │ │ │ ├── crimson_wyrm.gd │ │ │ ├── cruel_fire.gd │ │ │ ├── cursed_grounds.gd │ │ │ ├── cute_small_spider.gd │ │ │ ├── dark_battery.gd │ │ │ ├── death_knight.gd │ │ │ ├── dimensional_flux_collector.gd │ │ │ ├── dragon_sorcerer.gd │ │ │ ├── drake_whisperer.gd │ │ │ ├── dreadlord.gd │ │ │ ├── dutchmans_grave.gd │ │ │ ├── dwarven_forgery.gd │ │ │ ├── ebonfrost_crystal.gd │ │ │ ├── embershell_turtle_hatchling.gd │ │ │ ├── empty_tower_behavior.gd │ │ │ ├── energy_junction.gd │ │ │ ├── essence_of_fury.gd │ │ │ ├── felweed.gd │ │ │ ├── fenced_flames.gd │ │ │ ├── fiery_dog.gd │ │ │ ├── fire_battery.gd │ │ │ ├── fire_star.gd │ │ │ ├── firestorm_cell.gd │ │ │ ├── fisherman.gd │ │ │ ├── forest_archer.gd │ │ │ ├── forest_protectress.gd │ │ │ ├── forest_troll.gd │ │ │ ├── frost_root.gd │ │ │ ├── frosty_rock.gd │ │ │ ├── frozen_well.gd │ │ │ ├── garden_of_eden.gd │ │ │ ├── gatling_gun.gd │ │ │ ├── genis_sage.gd │ │ │ ├── geothermal_extractor.gd │ │ │ ├── glaive_master.gd │ │ │ ├── glowing_solar_orb.gd │ │ │ ├── gnoll_thunder_mage.gd │ │ │ ├── goblin_stronghold.gd │ │ │ ├── grab-o-bot.gd │ │ │ ├── green_dragon_roost.gd │ │ │ ├── green_lightning.gd │ │ │ ├── greyfang.gd │ │ │ ├── gryphon_rider.gd │ │ │ ├── hall_of_souls.gd │ │ │ ├── harby.gd │ │ │ ├── harpy_witch.gd │ │ │ ├── haunted_rubble.gd │ │ │ ├── healing_obelisk.gd │ │ │ ├── helicopter_zone.gd │ │ │ ├── hell_bat.gd │ │ │ ├── holy_energy.gd │ │ │ ├── ice_battery.gd │ │ │ ├── icy_core.gd │ │ │ ├── icy_skulls.gd │ │ │ ├── icy_spirit.gd │ │ │ ├── igloo.gd │ │ │ ├── inexperienced_huntress.gd │ │ │ ├── inflamed_stone.gd │ │ │ ├── initiate_elementalist.gd │ │ │ ├── it.gd │ │ │ ├── jungle_stalker.gd │ │ │ ├── kraken.gd │ │ │ ├── lesser_elemental_ghost.gd │ │ │ ├── lesser_priest.gd │ │ │ ├── lesser_skeletal_mage.gd │ │ │ ├── lesser_wolves_den.gd │ │ │ ├── library_of_alexandria.gd │ │ │ ├── lich_king.gd │ │ │ ├── lightning_eye.gd │ │ │ ├── lightning_generator.gd │ │ │ ├── lightning_totem.gd │ │ │ ├── little_phoenix.gd │ │ │ ├── living_volcano.gd │ │ │ ├── lunar_emitter.gd │ │ │ ├── lunar_sentinel.gd │ │ │ ├── magic_battery.gd │ │ │ ├── magic_mushroom.gd │ │ │ ├── magna_warrior.gd │ │ │ ├── mana-touched_drake.gd │ │ │ ├── marine.gd │ │ │ ├── meteor_totem.gd │ │ │ ├── militia_watchtower.gd │ │ │ ├── miner.gd │ │ │ ├── minor_magic_ruin.gd │ │ │ ├── mister_fireflies.gd │ │ │ ├── monolith_of_chaos.gd │ │ │ ├── morphling.gd │ │ │ ├── mossy_acid_sprayer.gd │ │ │ ├── mud_golem.gd │ │ │ ├── nature_sprites.gd │ │ │ ├── necromantic_altar.gd │ │ │ ├── nortrom_the_silencer.gd │ │ │ ├── nuclear_missile_launcher.gd │ │ │ ├── obelisk_of_fortuity.gd │ │ │ ├── owl_of_wisdom.gd │ │ │ ├── particle_accelerator.gd │ │ │ ├── phantom.gd │ │ │ ├── plagued_crypt.gd │ │ │ ├── planar_gate.gd │ │ │ ├── poison_battery.gd │ │ │ ├── polar_bear_cub.gd │ │ │ ├── portal_to_swine_purgatory.gd │ │ │ ├── prince_of_lightning.gd │ │ │ ├── princess_of_light.gd │ │ │ ├── razorboar_thornweaver.gd │ │ │ ├── red_ball_lightning.gd │ │ │ ├── regenerating_well.gd │ │ │ ├── rooted_chasm.gd │ │ │ ├── rotted_flashing_grave.gd │ │ │ ├── rowing_boat.gd │ │ │ ├── ruined_wind_tower.gd │ │ │ ├── rundown_iron_sentry.gd │ │ │ ├── sacred_altar.gd │ │ │ ├── sacrificial_lamb.gd │ │ │ ├── safirons_cold_grave.gd │ │ │ ├── scales.gd │ │ │ ├── sea_turtle.gd │ │ │ ├── servant_of_the_twin_flames.gd │ │ │ ├── sewer_connection.gd │ │ │ ├── shadow.gd │ │ │ ├── shaman.gd │ │ │ ├── shard_of_souls.gd │ │ │ ├── silver_knight.gd │ │ │ ├── skink.gd │ │ │ ├── small_bug_nest.gd │ │ │ ├── small_fire_sprayer.gd │ │ │ ├── small_frost_fire.gd │ │ │ ├── small_ice_mine.gd │ │ │ ├── small_light.gd │ │ │ ├── small_ray_blaster.gd │ │ │ ├── small_serpent_ward.gd │ │ │ ├── small_torch.gd │ │ │ ├── sniper.gd │ │ │ ├── solar_collector.gd │ │ │ ├── solar_emitter.gd │ │ │ ├── sorceress.gd │ │ │ ├── soul_vault.gd │ │ │ ├── soulflame_device.gd │ │ │ ├── spell_collector.gd │ │ │ ├── spider_queen.gd │ │ │ ├── star_gazer.gd │ │ │ ├── storm_battery.gd │ │ │ ├── storm_coil.gd │ │ │ ├── storm_focus.gd │ │ │ ├── stormy_dog.gd │ │ │ ├── sun_crusader.gd │ │ │ ├── taita_the_hermit.gd │ │ │ ├── teacher.gd │ │ │ ├── tentacle_spawn.gd │ │ │ ├── the_conduit.gd │ │ │ ├── the_council_of_demons.gd │ │ │ ├── the_fire_lord.gd │ │ │ ├── the_frozen_wyrm.gd │ │ │ ├── the_furnace.gd │ │ │ ├── the_omnislasher.gd │ │ │ ├── the_steam_engine.gd │ │ │ ├── thief_apprentice.gd │ │ │ ├── tidewater_stream.gd │ │ │ ├── time_manipulator.gd │ │ │ ├── timevault.gd │ │ │ ├── tiny_storm_lantern.gd │ │ │ ├── tombstone.gd │ │ │ ├── tundra_stalker.gd │ │ │ ├── undisturbed_crypt.gd │ │ │ ├── valor.gd │ │ │ ├── village_witch.gd │ │ │ ├── void_drake.gd │ │ │ ├── vulshok_the_berserker.gd │ │ │ ├── warrior_of_light.gd │ │ │ ├── wild_warbeast.gd │ │ │ ├── witch_doctor.gd │ │ │ ├── wooden_trap.gd │ │ │ ├── xeno_research_facility.gd │ │ │ ├── ymir.gd │ │ │ ├── young_northern_troll.gd │ │ │ ├── zealot.gd │ │ │ └── zeus.gd │ │ ├── tower_preview.gd │ │ ├── tower_preview.tscn │ │ └── tower_sprites/ │ │ ├── abandoned_pit_1.tscn │ │ ├── abandoned_pit_2.tscn │ │ ├── abandoned_pit_3.tscn │ │ ├── abandoned_pit_4.tscn │ │ ├── abandoned_pit_5.tscn │ │ ├── abominable_snowman_1.tscn │ │ ├── abominable_snowman_2.tscn │ │ ├── abominable_snowman_3.tscn │ │ ├── abominable_snowman_4.tscn │ │ ├── abominable_snowman_5.tscn │ │ ├── afflicted_obelisk_1.tscn │ │ ├── afflicted_obelisk_2.tscn │ │ ├── afflicted_obelisk_3.tscn │ │ ├── afflicted_obelisk_4.tscn │ │ ├── ancient_energy_converter_1.tscn │ │ ├── annoyed_tree_1.tscn │ │ ├── annoyed_tree_2.tscn │ │ ├── annoyed_tree_3.tscn │ │ ├── annoyed_tree_4.tscn │ │ ├── arcane_storm_1.tscn │ │ ├── area_roaster_1.tscn │ │ ├── area_roaster_2.tscn │ │ ├── area_roaster_3.tscn │ │ ├── ash_geyser_1.tscn │ │ ├── ash_geyser_2.tscn │ │ ├── ash_geyser_3.tscn │ │ ├── ash_geyser_4.tscn │ │ ├── ash_geyser_5.tscn │ │ ├── astral_lantern_1.tscn │ │ ├── astral_lantern_2.tscn │ │ ├── astral_lantern_3.tscn │ │ ├── astral_lantern_4.tscn │ │ ├── astral_rift_1.tscn │ │ ├── baby_plant_1.tscn │ │ ├── baby_plant_2.tscn │ │ ├── baby_plant_3.tscn │ │ ├── baby_plant_4.tscn │ │ ├── baby_plant_5.tscn │ │ ├── baby_plant_6.tscn │ │ ├── baby_tuskin_1.tscn │ │ ├── baby_tuskin_2.tscn │ │ ├── baby_tuskin_3.tscn │ │ ├── ball_lightning_accelerator_1.tscn │ │ ├── ball_lightning_accelerator_2.tscn │ │ ├── basic_knowledge_1.tscn │ │ ├── basic_knowledge_2.tscn │ │ ├── basic_knowledge_3.tscn │ │ ├── basic_knowledge_4.tscn │ │ ├── basic_knowledge_5.tscn │ │ ├── black_dragon_roost_1.tscn │ │ ├── black_rock_totem_1.tscn │ │ ├── bomb_turret_1.tscn │ │ ├── bomb_turret_2.tscn │ │ ├── bone_shrine_1.tscn │ │ ├── bone_shrine_2.tscn │ │ ├── bone_shrine_3.tscn │ │ ├── bonk_the_living_mountain_1.tscn │ │ ├── broken_cage_1.tscn │ │ ├── broken_cage_2.tscn │ │ ├── broken_cage_3.tscn │ │ ├── broken_cage_4.tscn │ │ ├── broken_cage_5.tscn │ │ ├── broken_circle_of_wind_1.tscn │ │ ├── broken_circle_of_wind_2.tscn │ │ ├── broken_circle_of_wind_3.tscn │ │ ├── broken_circle_of_wind_4.tscn │ │ ├── broken_circle_of_wind_5.tscn │ │ ├── broken_fire_pit_1.tscn │ │ ├── broken_fire_pit_2.tscn │ │ ├── broken_fire_pit_3.tscn │ │ ├── broken_fire_pit_4.tscn │ │ ├── broken_fire_pit_5.tscn │ │ ├── broken_lightning_rod_1.tscn │ │ ├── broken_lightning_rod_2.tscn │ │ ├── broken_lightning_rod_3.tscn │ │ ├── broken_lightning_rod_4.tscn │ │ ├── broken_lightning_rod_5.tscn │ │ ├── bronze_dragon_roost_1.tscn │ │ ├── buried_soul_1.tscn │ │ ├── buried_soul_2.tscn │ │ ├── buried_soul_3.tscn │ │ ├── buried_soul_4.tscn │ │ ├── burning_watchtower_1.tscn │ │ ├── burning_watchtower_2.tscn │ │ ├── burning_watchtower_3.tscn │ │ ├── burning_watchtower_4.tscn │ │ ├── burrow_1.tscn │ │ ├── burrow_2.tscn │ │ ├── burrow_3.tscn │ │ ├── burrow_4.tscn │ │ ├── caged_fire_1.tscn │ │ ├── caged_fire_2.tscn │ │ ├── cenarion_1.tscn │ │ ├── chaining_storm_1.tscn │ │ ├── chaos_warlock_1.tscn │ │ ├── chaos_warlock_2.tscn │ │ ├── charged_obelisk_1.tscn │ │ ├── chilled_spire_1.tscn │ │ ├── chilled_spire_2.tscn │ │ ├── cloud_warrior_1.tscn │ │ ├── cloud_warrior_2.tscn │ │ ├── cloud_warrior_3.tscn │ │ ├── cloud_warrior_4.tscn │ │ ├── cloud_warrior_5.tscn │ │ ├── cloudy_temple_of_absorption_1.tscn │ │ ├── coconut_sapling_1.tscn │ │ ├── coconut_sapling_2.tscn │ │ ├── coin_machine_1.tscn │ │ ├── coin_machine_2.tscn │ │ ├── cold_obelisk_1.tscn │ │ ├── cold_obelisk_2.tscn │ │ ├── cold_obelisk_3.tscn │ │ ├── cold_troll_1.tscn │ │ ├── cold_troll_2.tscn │ │ ├── cold_troll_3.tscn │ │ ├── cold_troll_4.tscn │ │ ├── contraption_1.tscn │ │ ├── contraption_2.tscn │ │ ├── contraption_3.tscn │ │ ├── contraption_4.tscn │ │ ├── crimson_wyrm_1.tscn │ │ ├── cruel_fire_1.tscn │ │ ├── cruel_fire_2.tscn │ │ ├── cruel_fire_3.tscn │ │ ├── cursed_grounds_1.tscn │ │ ├── cursed_grounds_2.tscn │ │ ├── cursed_grounds_3.tscn │ │ ├── cute_small_spider_1.tscn │ │ ├── cute_small_spider_2.tscn │ │ ├── cute_small_spider_3.tscn │ │ ├── cute_small_spider_4.tscn │ │ ├── dark_battery_1.tscn │ │ ├── dark_battery_2.tscn │ │ ├── dark_battery_3.tscn │ │ ├── dark_fire_pit_1.tscn │ │ ├── dark_fire_pit_2.tscn │ │ ├── dark_fire_pit_3.tscn │ │ ├── dark_fire_pit_4.tscn │ │ ├── dark_fire_pit_5.tscn │ │ ├── death_knight_1.tscn │ │ ├── dimensional_flux_collector_1.tscn │ │ ├── dragon_sorcerer_1.tscn │ │ ├── drake_whisperer_1.tscn │ │ ├── dreadlord_1.tscn │ │ ├── dutchmans_grave_1.tscn │ │ ├── dwarven_forgery_1.tscn │ │ ├── ebonfrost_crystal_1.tscn │ │ ├── embershell_turtle_hatchling_1.tscn │ │ ├── embershell_turtle_hatchling_2.tscn │ │ ├── embershell_turtle_hatchling_3.tscn │ │ ├── embershell_turtle_hatchling_4.tscn │ │ ├── embershell_turtle_hatchling_5.tscn │ │ ├── energy_junction_1.tscn │ │ ├── energy_junction_2.tscn │ │ ├── energy_junction_3.tscn │ │ ├── essence_of_fury_1.tscn │ │ ├── essence_of_fury_2.tscn │ │ ├── essence_of_fury_3.tscn │ │ ├── essence_of_fury_4.tscn │ │ ├── essence_of_fury_5.tscn │ │ ├── felweed_1.tscn │ │ ├── felweed_2.tscn │ │ ├── felweed_3.tscn │ │ ├── felweed_4.tscn │ │ ├── felweed_5.tscn │ │ ├── felweed_6.tscn │ │ ├── fenced_flames_1.tscn │ │ ├── fenced_flames_2.tscn │ │ ├── fenced_flames_3.tscn │ │ ├── fenced_flames_4.tscn │ │ ├── fiery_dog_1.tscn │ │ ├── fiery_dog_2.tscn │ │ ├── fiery_dog_3.tscn │ │ ├── fiery_dog_4.tscn │ │ ├── fiery_dog_5.tscn │ │ ├── fiery_pebble_1.tscn │ │ ├── fiery_pebble_2.tscn │ │ ├── fiery_pebble_3.tscn │ │ ├── fiery_pebble_4.tscn │ │ ├── fiery_pebble_5.tscn │ │ ├── fiery_pebble_6.tscn │ │ ├── fire_battery_1.tscn │ │ ├── fire_battery_2.tscn │ │ ├── fire_battery_3.tscn │ │ ├── fire_star_1.tscn │ │ ├── firestorm_cell_1.tscn │ │ ├── firestorm_cell_2.tscn │ │ ├── firestorm_cell_3.tscn │ │ ├── firestorm_cell_4.tscn │ │ ├── fisherman_1.tscn │ │ ├── forest_archer_1.tscn │ │ ├── forest_archer_2.tscn │ │ ├── forest_archer_3.tscn │ │ ├── forest_protectress_1.tscn │ │ ├── forest_troll_1.tscn │ │ ├── forest_troll_2.tscn │ │ ├── forest_troll_3.tscn │ │ ├── frost_root_1.tscn │ │ ├── frost_root_2.tscn │ │ ├── frost_root_3.tscn │ │ ├── frost_root_4.tscn │ │ ├── frost_root_5.tscn │ │ ├── frost_root_6.tscn │ │ ├── frosty_rock_1.tscn │ │ ├── frosty_rock_2.tscn │ │ ├── frosty_rock_3.tscn │ │ ├── frosty_rock_4.tscn │ │ ├── frozen_well_1.tscn │ │ ├── garden_of_eden_1.tscn │ │ ├── gatling_gun_1.tscn │ │ ├── genis_sage_1.tscn │ │ ├── geothermal_extractor_1.tscn │ │ ├── geothermal_extractor_2.tscn │ │ ├── glaive_master_1.tscn │ │ ├── glowing_solar_orb_1.tscn │ │ ├── glowing_solar_orb_2.tscn │ │ ├── glowing_solar_orb_3.tscn │ │ ├── glowing_solar_orb_4.tscn │ │ ├── glowing_solar_orb_5.tscn │ │ ├── gnoll_thunder_mage_1.tscn │ │ ├── gnoll_thunder_mage_2.tscn │ │ ├── gnoll_thunder_mage_3.tscn │ │ ├── goblin_stronghold_1.tscn │ │ ├── grab-o-bot_1.tscn │ │ ├── green_dragon_roost_1.tscn │ │ ├── green_lightning_1.tscn │ │ ├── green_lightning_2.tscn │ │ ├── green_lightning_3.tscn │ │ ├── green_lightning_4.tscn │ │ ├── greyfang_1.tscn │ │ ├── gryphon_rider_1.tscn │ │ ├── hall_of_souls_1.tscn │ │ ├── hall_of_souls_2.tscn │ │ ├── hall_of_souls_3.tscn │ │ ├── harby_1.tscn │ │ ├── harpy_witch_1.tscn │ │ ├── harpy_witch_2.tscn │ │ ├── haunted_rubble_1.tscn │ │ ├── haunted_rubble_2.tscn │ │ ├── haunted_rubble_3.tscn │ │ ├── haunted_rubble_4.tscn │ │ ├── haunted_rubble_5.tscn │ │ ├── healing_obelisk_1.tscn │ │ ├── healing_obelisk_2.tscn │ │ ├── healing_obelisk_3.tscn │ │ ├── healing_obelisk_4.tscn │ │ ├── helicopter_zone_1.tscn │ │ ├── hell_bat_1.tscn │ │ ├── hell_bat_2.tscn │ │ ├── hell_bat_3.tscn │ │ ├── holy_energy_1.tscn │ │ ├── ice_battery_1.tscn │ │ ├── ice_battery_2.tscn │ │ ├── ice_battery_3.tscn │ │ ├── icy_core_1.tscn │ │ ├── icy_core_2.tscn │ │ ├── icy_skulls_1.tscn │ │ ├── icy_skulls_2.tscn │ │ ├── icy_skulls_3.tscn │ │ ├── icy_skulls_4.tscn │ │ ├── icy_spirit_1.tscn │ │ ├── icy_spirit_2.tscn │ │ ├── icy_spirit_3.tscn │ │ ├── igloo_1.tscn │ │ ├── igloo_2.tscn │ │ ├── igloo_3.tscn │ │ ├── inexperienced_huntress_1.tscn │ │ ├── inexperienced_huntress_2.tscn │ │ ├── inexperienced_huntress_3.tscn │ │ ├── inflamed_stone_1.tscn │ │ ├── inflamed_stone_2.tscn │ │ ├── inflamed_stone_3.tscn │ │ ├── initiate_elementalist_1.tscn │ │ ├── initiate_elementalist_2.tscn │ │ ├── initiate_elementalist_3.tscn │ │ ├── initiate_elementalist_4.tscn │ │ ├── it_1.tscn │ │ ├── jungle_stalker_1.tscn │ │ ├── jungle_stalker_2.tscn │ │ ├── jungle_stalker_3.tscn │ │ ├── kraken_1.tscn │ │ ├── lesser_astral_defender_1.tscn │ │ ├── lesser_astral_defender_2.tscn │ │ ├── lesser_astral_defender_3.tscn │ │ ├── lesser_astral_defender_4.tscn │ │ ├── lesser_astral_defender_5.tscn │ │ ├── lesser_dark_defender_1.tscn │ │ ├── lesser_dark_defender_2.tscn │ │ ├── lesser_dark_defender_3.tscn │ │ ├── lesser_dark_defender_4.tscn │ │ ├── lesser_dark_defender_5.tscn │ │ ├── lesser_elemental_ghost_1.tscn │ │ ├── lesser_elemental_ghost_2.tscn │ │ ├── lesser_elemental_ghost_3.tscn │ │ ├── lesser_elemental_ghost_4.tscn │ │ ├── lesser_elemental_ghost_5.tscn │ │ ├── lesser_flamy_defender_1.tscn │ │ ├── lesser_flamy_defender_2.tscn │ │ ├── lesser_flamy_defender_3.tscn │ │ ├── lesser_flamy_defender_4.tscn │ │ ├── lesser_flamy_defender_5.tscn │ │ ├── lesser_ice_defender_1.tscn │ │ ├── lesser_ice_defender_2.tscn │ │ ├── lesser_ice_defender_3.tscn │ │ ├── lesser_ice_defender_4.tscn │ │ ├── lesser_ice_defender_5.tscn │ │ ├── lesser_iron_defender_1.tscn │ │ ├── lesser_iron_defender_2.tscn │ │ ├── lesser_iron_defender_3.tscn │ │ ├── lesser_iron_defender_4.tscn │ │ ├── lesser_iron_defender_5.tscn │ │ ├── lesser_natural_defender_1.tscn │ │ ├── lesser_natural_defender_2.tscn │ │ ├── lesser_natural_defender_3.tscn │ │ ├── lesser_natural_defender_4.tscn │ │ ├── lesser_natural_defender_5.tscn │ │ ├── lesser_priest_1.tscn │ │ ├── lesser_priest_2.tscn │ │ ├── lesser_priest_3.tscn │ │ ├── lesser_priest_4.tscn │ │ ├── lesser_priest_5.tscn │ │ ├── lesser_skeletal_mage_1.tscn │ │ ├── lesser_skeletal_mage_2.tscn │ │ ├── lesser_skeletal_mage_3.tscn │ │ ├── lesser_skeletal_mage_4.tscn │ │ ├── lesser_storm_defender_1.tscn │ │ ├── lesser_storm_defender_2.tscn │ │ ├── lesser_storm_defender_3.tscn │ │ ├── lesser_storm_defender_4.tscn │ │ ├── lesser_storm_defender_5.tscn │ │ ├── lesser_wolves_den_1.tscn │ │ ├── lesser_wolves_den_2.tscn │ │ ├── lesser_wolves_den_3.tscn │ │ ├── library_of_alexandria_1.tscn │ │ ├── lich_king_1.tscn │ │ ├── lightning_eye_1.tscn │ │ ├── lightning_generator_1.tscn │ │ ├── lightning_generator_2.tscn │ │ ├── lightning_generator_3.tscn │ │ ├── lightning_generator_4.tscn │ │ ├── lightning_totem_1.tscn │ │ ├── lightning_totem_2.tscn │ │ ├── lightning_totem_3.tscn │ │ ├── little_phoenix_1.tscn │ │ ├── little_phoenix_2.tscn │ │ ├── little_phoenix_3.tscn │ │ ├── living_volcano_1.tscn │ │ ├── lunar_emitter_1.tscn │ │ ├── lunar_emitter_2.tscn │ │ ├── lunar_sentinel_1.tscn │ │ ├── lunar_sentinel_2.tscn │ │ ├── lunar_sentinel_3.tscn │ │ ├── lunar_sentinel_4.tscn │ │ ├── magic_battery_1.tscn │ │ ├── magic_battery_2.tscn │ │ ├── magic_battery_3.tscn │ │ ├── magic_mushroom_1.tscn │ │ ├── magna_warrior_1.tscn │ │ ├── magna_warrior_2.tscn │ │ ├── magna_warrior_3.tscn │ │ ├── magna_warrior_4.tscn │ │ ├── magna_warrior_5.tscn │ │ ├── mana-touched_drake_1.tscn │ │ ├── mana-touched_drake_2.tscn │ │ ├── mana-touched_drake_3.tscn │ │ ├── mana-touched_drake_4.tscn │ │ ├── marine_1.tscn │ │ ├── marine_2.tscn │ │ ├── meteor_totem_1.tscn │ │ ├── militia_watchtower_1.tscn │ │ ├── militia_watchtower_2.tscn │ │ ├── militia_watchtower_3.tscn │ │ ├── militia_watchtower_4.tscn │ │ ├── miner_1.tscn │ │ ├── miner_2.tscn │ │ ├── miner_3.tscn │ │ ├── minor_magic_ruin_1.tscn │ │ ├── minor_magic_ruin_2.tscn │ │ ├── minor_magic_ruin_3.tscn │ │ ├── minor_magic_ruin_4.tscn │ │ ├── minor_magic_ruin_5.tscn │ │ ├── minor_magic_ruin_6.tscn │ │ ├── mister_fireflies_1.tscn │ │ ├── mister_fireflies_2.tscn │ │ ├── mister_fireflies_3.tscn │ │ ├── monolith_of_chaos_1.tscn │ │ ├── morphling_1.tscn │ │ ├── mossy_acid_sprayer_1.tscn │ │ ├── mossy_acid_sprayer_2.tscn │ │ ├── mossy_acid_sprayer_3.tscn │ │ ├── mossy_acid_sprayer_4.tscn │ │ ├── mossy_acid_sprayer_5.tscn │ │ ├── mud_golem_1.tscn │ │ ├── nature_sprites_1.tscn │ │ ├── nature_sprites_2.tscn │ │ ├── nature_sprites_3.tscn │ │ ├── necromantic_altar_1.tscn │ │ ├── necromantic_altar_2.tscn │ │ ├── necromantic_altar_3.tscn │ │ ├── necromantic_altar_4.tscn │ │ ├── nortrom_the_silencer_1.tscn │ │ ├── nuclear_missile_launcher_1.tscn │ │ ├── obelisk_of_fortuity_1.tscn │ │ ├── obelisk_of_fortuity_2.tscn │ │ ├── obelisk_of_fortuity_3.tscn │ │ ├── obelisk_of_fortuity_4.tscn │ │ ├── obelisk_of_fortuity_5.tscn │ │ ├── owl_of_wisdom_1.tscn │ │ ├── owl_of_wisdom_2.tscn │ │ ├── particle_accelerator_1.tscn │ │ ├── particle_accelerator_2.tscn │ │ ├── particle_accelerator_3.tscn │ │ ├── phantom_1.tscn │ │ ├── phantom_2.tscn │ │ ├── phantom_3.tscn │ │ ├── phantom_4.tscn │ │ ├── phantom_5.tscn │ │ ├── plagued_crypt_1.tscn │ │ ├── planar_gate_1.tscn │ │ ├── poison_battery_1.tscn │ │ ├── poison_battery_2.tscn │ │ ├── poison_battery_3.tscn │ │ ├── polar_bear_cub_1.tscn │ │ ├── polar_bear_cub_2.tscn │ │ ├── polar_bear_cub_3.tscn │ │ ├── portal_to_swine_purgatory_1.tscn │ │ ├── portal_to_swine_purgatory_2.tscn │ │ ├── portal_to_swine_purgatory_3.tscn │ │ ├── prince_of_lightning_1.tscn │ │ ├── prince_of_lightning_2.tscn │ │ ├── princess_of_light_1.tscn │ │ ├── princess_of_light_2.tscn │ │ ├── razorboar_thornweaver_1.tscn │ │ ├── razorboar_thornweaver_2.tscn │ │ ├── razorboar_thornweaver_3.tscn │ │ ├── red_ball_lightning_1.tscn │ │ ├── red_ball_lightning_2.tscn │ │ ├── regenerating_well_1.tscn │ │ ├── regenerating_well_2.tscn │ │ ├── regenerating_well_3.tscn │ │ ├── rooted_chasm_1.tscn │ │ ├── rooted_chasm_2.tscn │ │ ├── rooted_chasm_3.tscn │ │ ├── rooted_chasm_4.tscn │ │ ├── rotted_flashing_grave_1.tscn │ │ ├── rotted_flashing_grave_2.tscn │ │ ├── rotted_flashing_grave_3.tscn │ │ ├── rotted_flashing_grave_4.tscn │ │ ├── rotted_flashing_grave_5.tscn │ │ ├── rotted_flashing_grave_6.tscn │ │ ├── rowing_boat_1.tscn │ │ ├── rowing_boat_2.tscn │ │ ├── rowing_boat_3.tscn │ │ ├── rowing_boat_4.tscn │ │ ├── ruined_monolith_1.tscn │ │ ├── ruined_monolith_2.tscn │ │ ├── ruined_monolith_3.tscn │ │ ├── ruined_monolith_4.tscn │ │ ├── ruined_monolith_5.tscn │ │ ├── ruined_monolith_6.tscn │ │ ├── ruined_storm_cap_1.tscn │ │ ├── ruined_storm_cap_2.tscn │ │ ├── ruined_storm_cap_3.tscn │ │ ├── ruined_storm_cap_4.tscn │ │ ├── ruined_storm_cap_5.tscn │ │ ├── ruined_storm_cap_6.tscn │ │ ├── ruined_sun_pedestal_1.tscn │ │ ├── ruined_sun_pedestal_2.tscn │ │ ├── ruined_sun_pedestal_3.tscn │ │ ├── ruined_sun_pedestal_4.tscn │ │ ├── ruined_sun_pedestal_5.tscn │ │ ├── ruined_sun_pedestal_6.tscn │ │ ├── ruined_sun_pedestal_7.tscn │ │ ├── ruined_wind_tower_1.tscn │ │ ├── ruined_wind_tower_2.tscn │ │ ├── ruined_wind_tower_3.tscn │ │ ├── ruined_wind_tower_4.tscn │ │ ├── rundown_iron_sentry_1.tscn │ │ ├── rundown_iron_sentry_2.tscn │ │ ├── rundown_iron_sentry_3.tscn │ │ ├── sacred_altar_1.tscn │ │ ├── sacrificial_lamb_1.tscn │ │ ├── sacrificial_lamb_2.tscn │ │ ├── safirons_cold_grave_1.tscn │ │ ├── scales_1.tscn │ │ ├── sea_turtle_1.tscn │ │ ├── sea_turtle_2.tscn │ │ ├── sea_turtle_3.tscn │ │ ├── servant_of_the_twin_flames_1.tscn │ │ ├── servant_of_the_twin_flames_2.tscn │ │ ├── sewer_connection_1.tscn │ │ ├── sewer_connection_2.tscn │ │ ├── sewer_connection_3.tscn │ │ ├── sewer_connection_4.tscn │ │ ├── shadow_1.tscn │ │ ├── shaman_1.tscn │ │ ├── shaman_2.tscn │ │ ├── shaman_3.tscn │ │ ├── shard_of_souls_1.tscn │ │ ├── shard_of_souls_2.tscn │ │ ├── silver_knight_1.tscn │ │ ├── silver_knight_2.tscn │ │ ├── skink_1.tscn │ │ ├── skink_2.tscn │ │ ├── skink_3.tscn │ │ ├── skink_4.tscn │ │ ├── skink_5.tscn │ │ ├── small_brazier_1.tscn │ │ ├── small_brazier_2.tscn │ │ ├── small_brazier_3.tscn │ │ ├── small_brazier_4.tscn │ │ ├── small_bug_nest_1.tscn │ │ ├── small_bug_nest_2.tscn │ │ ├── small_bug_nest_3.tscn │ │ ├── small_bug_nest_4.tscn │ │ ├── small_cactus_1.tscn │ │ ├── small_cactus_2.tscn │ │ ├── small_cactus_3.tscn │ │ ├── small_cactus_4.tscn │ │ ├── small_cactus_5.tscn │ │ ├── small_cactus_6.tscn │ │ ├── small_fire_sprayer_1.tscn │ │ ├── small_fire_sprayer_2.tscn │ │ ├── small_fire_sprayer_3.tscn │ │ ├── small_fire_sprayer_4.tscn │ │ ├── small_fire_sprayer_5.tscn │ │ ├── small_fire_sprayer_6.tscn │ │ ├── small_frost_fire_1.tscn │ │ ├── small_frost_fire_2.tscn │ │ ├── small_frost_fire_3.tscn │ │ ├── small_frost_fire_4.tscn │ │ ├── small_frost_fire_5.tscn │ │ ├── small_frozen_mushroom_1.tscn │ │ ├── small_frozen_mushroom_2.tscn │ │ ├── small_frozen_mushroom_3.tscn │ │ ├── small_frozen_mushroom_4.tscn │ │ ├── small_frozen_mushroom_5.tscn │ │ ├── small_ice_mine_1.tscn │ │ ├── small_ice_mine_2.tscn │ │ ├── small_ice_mine_3.tscn │ │ ├── small_ice_mine_4.tscn │ │ ├── small_light_1.tscn │ │ ├── small_light_2.tscn │ │ ├── small_light_3.tscn │ │ ├── small_light_4.tscn │ │ ├── small_light_5.tscn │ │ ├── small_pocket_rocket_1.tscn │ │ ├── small_pocket_rocket_2.tscn │ │ ├── small_pocket_rocket_3.tscn │ │ ├── small_pocket_rocket_4.tscn │ │ ├── small_pocket_rocket_5.tscn │ │ ├── small_ray_blaster_1.tscn │ │ ├── small_ray_blaster_2.tscn │ │ ├── small_ray_blaster_3.tscn │ │ ├── small_ray_blaster_4.tscn │ │ ├── small_ray_blaster_5.tscn │ │ ├── small_serpent_ward_1.tscn │ │ ├── small_serpent_ward_2.tscn │ │ ├── small_serpent_ward_3.tscn │ │ ├── small_serpent_ward_4.tscn │ │ ├── small_torch_1.tscn │ │ ├── small_torch_2.tscn │ │ ├── small_torch_3.tscn │ │ ├── small_torch_4.tscn │ │ ├── small_torch_5.tscn │ │ ├── sniper_1.tscn │ │ ├── sniper_2.tscn │ │ ├── sniper_3.tscn │ │ ├── sniper_4.tscn │ │ ├── snowy_pebble_1.tscn │ │ ├── snowy_pebble_2.tscn │ │ ├── snowy_pebble_3.tscn │ │ ├── snowy_pebble_4.tscn │ │ ├── snowy_pebble_5.tscn │ │ ├── snowy_pebble_6.tscn │ │ ├── solar_collector_1.tscn │ │ ├── solar_collector_2.tscn │ │ ├── solar_emitter_1.tscn │ │ ├── solar_emitter_2.tscn │ │ ├── sorceress_1.tscn │ │ ├── soul_vault_1.tscn │ │ ├── soulflame_device_1.tscn │ │ ├── spell_collector_1.tscn │ │ ├── spell_collector_2.tscn │ │ ├── spider_queen_1.tscn │ │ ├── star_gazer_1.tscn │ │ ├── star_gazer_2.tscn │ │ ├── star_gazer_3.tscn │ │ ├── star_gazer_4.tscn │ │ ├── storm_battery_1.tscn │ │ ├── storm_battery_2.tscn │ │ ├── storm_battery_3.tscn │ │ ├── storm_coil_1.tscn │ │ ├── storm_focus_1.tscn │ │ ├── stormy_dog_1.tscn │ │ ├── stormy_dog_2.tscn │ │ ├── stormy_dog_3.tscn │ │ ├── stormy_dog_4.tscn │ │ ├── stormy_dog_5.tscn │ │ ├── sun_crusader_1.tscn │ │ ├── sun_crusader_2.tscn │ │ ├── taita_the_hermit_1.tscn │ │ ├── teacher_1.tscn │ │ ├── teacher_2.tscn │ │ ├── teacher_3.tscn │ │ ├── teacher_4.tscn │ │ ├── teacher_5.tscn │ │ ├── teacher_6.tscn │ │ ├── tentacle_spawn_1.tscn │ │ ├── tentacle_spawn_2.tscn │ │ ├── tentacle_spawn_3.tscn │ │ ├── tentacle_spawn_4.tscn │ │ ├── tentacle_spawn_5.tscn │ │ ├── tentacle_spawn_6.tscn │ │ ├── the_conduit_1.tscn │ │ ├── the_council_of_demons_1.tscn │ │ ├── the_fire_lord_1.tscn │ │ ├── the_frozen_wyrm_1.tscn │ │ ├── the_furnace_1.tscn │ │ ├── the_omnislasher_1.tscn │ │ ├── the_steam_engine_1.tscn │ │ ├── thief_apprentice_1.tscn │ │ ├── thief_apprentice_2.tscn │ │ ├── thief_apprentice_3.tscn │ │ ├── thief_apprentice_4.tscn │ │ ├── thief_apprentice_5.tscn │ │ ├── tidewater_stream_1.tscn │ │ ├── time_manipulator_1.tscn │ │ ├── timevault_1.tscn │ │ ├── tiny_shrub_1.tscn │ │ ├── tiny_shrub_2.tscn │ │ ├── tiny_shrub_3.tscn │ │ ├── tiny_shrub_4.tscn │ │ ├── tiny_shrub_5.tscn │ │ ├── tiny_shrub_6.tscn │ │ ├── tiny_storm_lantern_1.tscn │ │ ├── tiny_storm_lantern_2.tscn │ │ ├── tiny_storm_lantern_3.tscn │ │ ├── tiny_storm_lantern_4.tscn │ │ ├── tombstone_1.tscn │ │ ├── tombstone_2.tscn │ │ ├── tombstone_3.tscn │ │ ├── tombstone_4.tscn │ │ ├── tombstone_5.tscn │ │ ├── tombstone_6.tscn │ │ ├── trash_heap_1.tscn │ │ ├── trash_heap_2.tscn │ │ ├── trash_heap_3.tscn │ │ ├── trash_heap_4.tscn │ │ ├── trash_heap_5.tscn │ │ ├── trash_heap_6.tscn │ │ ├── tree_stump_1.tscn │ │ ├── tree_stump_2.tscn │ │ ├── tree_stump_3.tscn │ │ ├── tree_stump_4.tscn │ │ ├── tree_stump_5.tscn │ │ ├── tundra_stalker_1.tscn │ │ ├── tundra_stalker_2.tscn │ │ ├── tundra_stalker_3.tscn │ │ ├── tundra_stalker_4.tscn │ │ ├── tundra_stalker_5.tscn │ │ ├── undisturbed_crypt_1.tscn │ │ ├── undisturbed_crypt_2.tscn │ │ ├── undisturbed_crypt_3.tscn │ │ ├── undisturbed_crypt_4.tscn │ │ ├── valor_1.tscn │ │ ├── village_witch_1.tscn │ │ ├── village_witch_2.tscn │ │ ├── village_witch_3.tscn │ │ ├── village_witch_4.tscn │ │ ├── void_drake_1.tscn │ │ ├── void_drake_2.tscn │ │ ├── vulshok_the_berserker_1.tscn │ │ ├── warrior_of_light_1.tscn │ │ ├── warrior_of_light_2.tscn │ │ ├── warrior_of_light_3.tscn │ │ ├── wild_warbeast_1.tscn │ │ ├── witch_doctor_1.tscn │ │ ├── wooden_trap_1.tscn │ │ ├── wooden_trap_2.tscn │ │ ├── wooden_trap_3.tscn │ │ ├── wooden_trap_4.tscn │ │ ├── wooden_trap_5.tscn │ │ ├── xeno_research_facility_1.tscn │ │ ├── ymir_1.tscn │ │ ├── young_northern_troll_1.tscn │ │ ├── young_northern_troll_2.tscn │ │ ├── young_northern_troll_3.tscn │ │ ├── zealot_1.tscn │ │ ├── zealot_2.tscn │ │ ├── zealot_3.tscn │ │ ├── zealot_4.tscn │ │ └── zeus_1.tscn │ ├── ui/ │ │ ├── buttons/ │ │ │ ├── ability_button.gd │ │ │ ├── ability_button.tscn │ │ │ ├── auto_mode_indicator.gd │ │ │ ├── auto_mode_indicator.tscn │ │ │ ├── autocast_button.gd │ │ │ ├── autocast_button.tscn │ │ │ ├── buff_group_button.gd │ │ │ ├── buff_group_button.tscn │ │ │ ├── builder_button.tscn │ │ │ ├── button_tooltip.gd │ │ │ ├── button_tooltip.tscn │ │ │ ├── button_with_rich_tooltip.gd │ │ │ ├── element_button.gd │ │ │ ├── element_button.tscn │ │ │ ├── empty_unit_button.gd │ │ │ ├── empty_unit_button.tscn │ │ │ ├── freshness_indicator.gd │ │ │ ├── freshness_indicator.tscn │ │ │ ├── inventory_slot_button.tscn │ │ │ ├── item_button.gd │ │ │ ├── item_button.tscn │ │ │ ├── item_button_inner.gd │ │ │ ├── menu_expanding_button.gd │ │ │ ├── menu_expanding_button.tscn │ │ │ ├── rarity_background.gd │ │ │ ├── rarity_background.tscn │ │ │ ├── recipe_button.gd │ │ │ ├── recipe_button.tscn │ │ │ ├── speed_button.tscn │ │ │ ├── time_indicator.gd │ │ │ ├── time_indicator.tscn │ │ │ ├── tower_button.gd │ │ │ ├── tower_button.tscn │ │ │ ├── wisdom_upgrade_button.gd │ │ │ └── wisdom_upgrade_button.tscn │ │ ├── dev_controls/ │ │ │ ├── dev_controls.gd │ │ │ ├── dev_controls.tscn │ │ │ ├── signals_control.gd │ │ │ ├── signals_control.tscn │ │ │ ├── wave_control.gd │ │ │ └── wave_control.tscn │ │ ├── filter_buttons/ │ │ │ ├── element_filter.gd │ │ │ ├── element_filter.tscn │ │ │ ├── filter_button.tscn │ │ │ ├── filter_button_element.gd │ │ │ ├── filter_button_element.tscn │ │ │ ├── filter_button_item_type.gd │ │ │ ├── filter_button_item_type.tscn │ │ │ ├── filter_button_rarity.gd │ │ │ ├── filter_button_rarity.tscn │ │ │ ├── item_type_filter.gd │ │ │ ├── item_type_filter.tscn │ │ │ ├── rarity_filter.gd │ │ │ └── rarity_filter.tscn │ │ ├── game_menu/ │ │ │ ├── credits_menu.gd │ │ │ ├── credits_menu.tscn │ │ │ ├── game_menu.gd │ │ │ ├── game_menu.tscn │ │ │ ├── help_menu.gd │ │ │ ├── help_menu.tscn │ │ │ ├── help_menu_tab.gd │ │ │ ├── help_menu_tab.tscn │ │ │ ├── settings_menu.gd │ │ │ └── settings_menu.tscn │ │ ├── hud/ │ │ │ ├── OneTimeHelpPopup.tscn │ │ │ ├── build_version_label.gd │ │ │ ├── build_version_label.tscn │ │ │ ├── builder_menu.gd │ │ │ ├── builder_menu.tscn │ │ │ ├── combat_log_window.gd │ │ │ ├── combat_log_window.tscn │ │ │ ├── desync_indicator.tscn │ │ │ ├── elements_menu.gd │ │ │ ├── elements_menu.tscn │ │ │ ├── floating_text.gd │ │ │ ├── floating_text.tscn │ │ │ ├── flying_item.gd │ │ │ ├── flying_item.tscn │ │ │ ├── game_speed_controller.gd │ │ │ ├── game_speed_controller.tscn │ │ │ ├── hud.gd │ │ │ ├── hud.tscn │ │ │ ├── label_with_rich_tooltip.gd │ │ │ ├── mission_track_indicator.gd │ │ │ ├── mission_track_indicator.tscn │ │ │ ├── mission_tracker_container.gd │ │ │ ├── mission_tracker_container.tscn │ │ │ ├── movable_window.gd │ │ │ ├── movable_window.tscn │ │ │ ├── multiplayer_pause_indicator.tscn │ │ │ ├── one_time_help_popup.gd │ │ │ ├── rich_text_label_with_rich_tooltip.gd │ │ │ ├── texture_rect_with_rich_tooltip.gd │ │ │ ├── tutorial_menu.gd │ │ │ └── tutorial_menu.tscn │ │ ├── item_stash_menu/ │ │ │ ├── item_stash_menu.gd │ │ │ └── item_stash_menu.tscn │ │ ├── player_resource_display/ │ │ │ ├── player_resource_display.gd │ │ │ ├── player_resource_display.tscn │ │ │ ├── resource_status_panel.gd │ │ │ └── resource_status_panel.tscn │ │ ├── title_screen/ │ │ │ ├── configure_singleplayer_menu.gd │ │ │ ├── configure_singleplayer_menu.tscn │ │ │ ├── connecting_to_server_indicator.tscn │ │ │ ├── encyclopedia_generic_tab.gd │ │ │ ├── encyclopedia_generic_tab.tscn │ │ │ ├── encyclopedia_items.gd │ │ │ ├── encyclopedia_items.tscn │ │ │ ├── encyclopedia_menu.gd │ │ │ ├── encyclopedia_menu.tscn │ │ │ ├── encyclopedia_towers.gd │ │ │ ├── encyclopedia_towers.tscn │ │ │ ├── export_exp_menu.gd │ │ │ ├── export_exp_menu.tscn │ │ │ ├── import_exp_menu.gd │ │ │ ├── import_exp_menu.tscn │ │ │ ├── lan_match/ │ │ │ │ ├── create_lan_match_menu.gd │ │ │ │ ├── create_lan_match_menu.tscn │ │ │ │ ├── lan_connect_menu.gd │ │ │ │ ├── lan_connect_menu.tscn │ │ │ │ ├── lan_lobby_menu.gd │ │ │ │ ├── lan_lobby_menu.tscn │ │ │ │ ├── setup_lan_game.gd │ │ │ │ └── setup_lan_game.tscn │ │ │ ├── match_config.gd │ │ │ ├── match_config_panel.gd │ │ │ ├── match_config_panel.tscn │ │ │ ├── message_popup.gd │ │ │ ├── message_popup.tscn │ │ │ ├── missions_menu/ │ │ │ │ ├── mission_card.gd │ │ │ │ ├── mission_card.tscn │ │ │ │ ├── missions_menu.gd │ │ │ │ ├── missions_menu.tscn │ │ │ │ ├── missions_menu_tab.gd │ │ │ │ └── missions_menu_tab.tscn │ │ │ ├── notification_panel.gd │ │ │ ├── notification_panel.tscn │ │ │ ├── online/ │ │ │ │ ├── create_online_match_menu.gd │ │ │ │ ├── create_online_match_menu.tscn │ │ │ │ ├── match_card.gd │ │ │ │ ├── match_card.tscn │ │ │ │ ├── online_lobby_menu.gd │ │ │ │ ├── online_lobby_menu.tscn │ │ │ │ ├── online_match_list_menu.gd │ │ │ │ ├── online_match_list_menu.tscn │ │ │ │ ├── setup_online_game.gd │ │ │ │ └── setup_online_game.tscn │ │ │ ├── profile_menu.gd │ │ │ ├── profile_menu.tscn │ │ │ ├── title_screen.gd │ │ │ ├── title_screen.tscn │ │ │ ├── wisdom_upgrade_menu.gd │ │ │ └── wisdom_upgrade_menu.tscn │ │ ├── top_left_menu/ │ │ │ ├── game_stats.gd │ │ │ ├── game_stats.tscn │ │ │ ├── plus_mode_label.gd │ │ │ ├── plus_mode_label.tscn │ │ │ ├── top_left_menu.gd │ │ │ ├── top_left_menu.tscn │ │ │ ├── wave_status.gd │ │ │ └── wave_status.tscn │ │ ├── tower_stash_menu/ │ │ │ ├── tower_stash_menu.gd │ │ │ └── tower_stash_menu.tscn │ │ └── unit_menu/ │ │ ├── buff_container.gd │ │ ├── buff_container.tscn │ │ ├── buff_display.gd │ │ ├── buff_display.tscn │ │ ├── buff_group_editor.gd │ │ ├── buff_group_editor.tscn │ │ ├── creep_details.gd │ │ ├── creep_details.tscn │ │ ├── creep_mini_details.gd │ │ ├── creep_mini_details.tscn │ │ ├── item_container_panel.gd │ │ ├── item_container_panel.tscn │ │ ├── mini_details_label.tscn │ │ ├── progress_bar_with_label.gd │ │ ├── progress_bar_with_label.tscn │ │ ├── tower_details.gd │ │ ├── tower_details.tscn │ │ ├── tower_mini_details.gd │ │ ├── tower_mini_details.tscn │ │ ├── unit_menu.gd │ │ └── unit_menu.tscn │ └── unit/ │ ├── dummy_unit.gd │ ├── dummy_unit.tscn │ ├── iterate.gd │ ├── selection_indicator.gd │ └── unit.gd ├── tools/ │ ├── README.md │ ├── check_buff_icons.gd │ ├── check_rng_sync.gd │ ├── check_translations.gd │ ├── check_tscn_paths_in_scripts.gd │ ├── convert_blender_export.gd │ ├── convert_tower_scene_to_sprite.gd │ ├── create_tilesheet.gd │ ├── cut_single_tile.gd │ ├── cut_tiles_into_connectors.gd │ ├── cut_tiles_into_decorations.gd │ ├── generate_censored_assets.gd │ ├── pack_sprite_sheet.gd │ ├── remove_empty_space_tilesheet.gd │ ├── remove_sheet_margins.gd │ ├── rename_files.gd │ ├── render_8_direction_sprites.py │ ├── separate_tilesheet.gd │ ├── show_missing_imports.py │ └── slice_sheet_into_rows.gd └── webrtc/ └── README.md ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/CODEOWNERS ================================================ * @Kvel2D @Praytic ================================================ FILE: .github/CODE_OF_CONDUCT.md ================================================ # Contributor Covenant Code of Conduct ## Our Pledge We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, caste, color, religion, or sexual identity and orientation. We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community. ## Our Standards Examples of behavior that contributes to a positive environment for our community include: * Demonstrating empathy and kindness toward other people * Being respectful of differing opinions, viewpoints, and experiences * Giving and gracefully accepting constructive feedback * Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience * Focusing on what is best not just for us as individuals, but for the overall community Examples of unacceptable behavior include: * The use of sexualized language or imagery, and sexual attention or advances of any kind * Trolling, insulting or derogatory comments, and personal or political attacks * Public or private harassment * Publishing others' private information, such as a physical or email address, without their explicit permission * Other conduct which could reasonably be considered inappropriate in a professional setting ## Enforcement Responsibilities Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful. Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate. ## Scope This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at [INSERT CONTACT METHOD]. All complaints will be reviewed and investigated promptly and fairly. All community leaders are obligated to respect the privacy and security of the reporter of any incident. ## Enforcement Guidelines Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct: ### 1. Correction **Community Impact**: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community. **Consequence**: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested. ### 2. Warning **Community Impact**: A violation through a single incident or series of actions. **Consequence**: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban. ### 3. Temporary Ban **Community Impact**: A serious violation of community standards, including sustained inappropriate behavior. **Consequence**: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban. ### 4. Permanent Ban **Community Impact**: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals. **Consequence**: A permanent ban from any sort of public interaction within the community. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.1, available at [https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. Community Impact Guidelines were inspired by [Mozilla's code of conduct enforcement ladder][Mozilla CoC]. For answers to common questions about this code of conduct, see the FAQ at [https://www.contributor-covenant.org/faq][FAQ]. Translations are available at [https://www.contributor-covenant.org/translations][translations]. [homepage]: https://www.contributor-covenant.org [v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html [Mozilla CoC]: https://github.com/mozilla/diversity [FAQ]: https://www.contributor-covenant.org/faq [translations]: https://www.contributor-covenant.org/translations ================================================ FILE: .github/CONTRIBUTING.md ================================================ # Contribution guidance ## How to contribute You can contribute in different ways: - Report a bug in Issues - Write a suggestion in Discussions - Submit a modification to source code! For the last one, you will need to download the Godot editor and obtain assets. Read below to find out how to obtain assets. ## Editing the game in the Godot editor First, obtain necessary files: 1. Download Godot editor *version 4.3* from the [Godot website](https://godotengine.org). 2. Clone the game repository using Git. 3. Download assets folder from this [Google Drive folder](https://drive.google.com/drive/folders/1U4wTjBu2qo1cInH3IAowsFC5yq56V5uQ?usp=sharing). 4. Copy and paste downloaded assets into 'assets' folder in the game repository. 5. You will see a popup asking whether you want to replace some files - press "Yes". Then, follow these steps to correctly import assets into Godot editor: 1. Open the game project in the Godot editor. 2. Wait for Godot editor to import assets. Open the "Output" window to confirm that the process is finished. There will be some errors - that's expected. 3. Press Ctrl-S to save changes. 4. Close Godot editor 5. Open a terminal with Git and run this command: $ git status. You should see that some files were modified (by Godot editor). 6. Run this command: $ git restore 7. Open the game project in the Godot editor again. 8. Wait for Godot editor to import assets. This time, there should be no errors. 9. Run this command again: $ git status. There should be no local changes if steps were followed correctly. Note: Public version of assets contains censored versions of item icons, tower icons and tower sprites. Such assets will look like they are a solid "blue" color. ## Copyright / Contributor License Agreement Any code you submit will become part of the repository and be distributed under the YouTD2 license. By submitting code to the project you agree that the code is your work and you can give it to the project. You also agree by submitting your code that you grant all transferrable rights to the code to the project maintainer, including for example re-licensing the code, modifying the code, and distributing it in source or binary forms. Specifically, this includes a requirement that you assign copyright to the project maintainer. For this reason, do not modify any copyright statements in files in any PRs. ================================================ FILE: .github/RCLONE.md ================================================ # Setup rclone Rclone is an advanced way to sync assets between your computer and google drive. Note that if you're using the public version of assets (assets_public), you can ignore this page and manually download files from drive. ## Setup steps 1. Install rclone from [here](https://rclone.org/downloads/). 2. Go to this [page](https://console.cloud.google.com/apis/credentials/oauthclient/909699965518-qt5c21qf6r7mr3rg26vkh6nml4s397e7.apps.googleusercontent.com?project=youtd2-385722). Copy and save `client_id` and `client_secret` to a text file. You will need them in the next step. 3. Follow the instructions [here](https://rclone.org/drive/) to configure `rcloud`. When prompted for `client_id` and `client_secret`, use the strings you have copied in the previous step. 4. Check that the remote is installed correctly. This command will print the installed ``. ``` rclone listremotes ``` 5. This command should print the list of folders insides assets in Google Drive. ``` rclone lsd :assets ``` 6. Transfer files to your local. ``` rclone sync -P --filter-from rclone-filter ":assets" "assets" ``` Now you should be able to run the project inside the Godot editor. If you make changes to a file inside assets folder, make sure to update Google Drive as well. There are two options. **First option - Google Drive upload.** 1. Upload a file to [Google Drive](https://drive.google.com/drive/u/1/folders/1V9GN1uoX9-mu2J5IoWPaNJU2aC_ejGIA) 2. Run `rclone sync` 3. Generate `.import` by opening the file in the editor 4. Commit `.import` file to remote **Second option - rclone copy.** 1. Move a file to `assets` folder 2. Generate `.import` by opening the file in the editor 3. Commit `.import` file to remote 4. Upload the file to . ``` rclone copy -v --filter-from rclone-filter assets :assets ``` ================================================ FILE: .github/TRANSLATING.md ================================================ # Translation guidance ## How to contribute to translation Translated text strings are stored in texts.csv. This file is not included on Github because it is too big. Download link: [texts.csv](https://drive.google.com/file/d/1dfaUKx5CoU9oVQQ4DgFVJH_twJ7M5k4Q/view?usp=drive_link). You can contribute translations like this: 1. Download the texts.csv file 2. Edit the texts.csv file 3. Create new column for your language, for example "es" 4. Add translated strings to that column Then you can submit the changes like this: 1. Create a new Issue on Github 2. Attach changed texts.csv file to the Issue 3. Follow the format from here: https://github.com/Praytic/youtd2/issues/483 ## How to add a new language This section is intended for developers. If you only want to contribute new translation texts, you can skip this section and let someone else handle adding the new language. 1. Find the locale code here: https://docs.godotengine.org/en/stable/tutorials/i18n/locales.html 2. Create new column in texts.csv with locale code in first row. 3. Close texts.csv and open Godot editor, confirm in editor log that texts.csv was reloaded. 4. Go to Menubar -> Project -> Project Settings -> Localization -> Add. Then add the translation file. 5. Edit src/enums/language.gd. Add new language to the following: "enm", "language_map", "language_option_map", "language_option_locale_map" 6. Open settings_menu.tscn in editor and modify the language OptionButton. Add new language and use the same index as the one in language.gd. 7. Start the game and confirm that new language works. ================================================ FILE: .github/workflows/github-actions-youtd.yml ================================================ name: build-and-publish # NOTE: disabled this export job because it doesn't work # with webrtc lib. Webrtc lib files need to be included # together with the game but this job for some reason fails # to do it. It only adds executable and .pck file to the # zip. # on: # push: # # Sequence of patterns matched against refs/heads # # branches: # # - workflow* # # Sequence of patterns matched against refs/tags # tags: # - v* jobs: setup: runs-on: ubuntu-latest name: cache repo with assets steps: - uses: actions/checkout@v3 - name: list assets in gdrive uses: wei/rclone@v1 with: args: lsf -R --filter-from rclone-filter "assets" | sort > .assets-list env: RCLONE_CONF: ${{ secrets.RCLONE_GDRIVE_CONF }} - name: restore cache uses: actions/cache@v3 with: path: assets key: assets-${{ hashFiles('.assets-list') }} - name: sync assets from gdrive via rclone uses: wei/rclone@v1 with: args: sync -P --filter-from rclone-filter "gdrive:assets" "assets" env: RCLONE_CONF: ${{ secrets.RCLONE_GDRIVE_CONF }} - name: upload repo with assets id: upload_repo_with_assets uses: actions/upload-artifact@v3 with: path: | ./* !.git/ name: repo-with-assets - name: generate version for the build id: version_generation run: | latest_tag=$(git ls-remote --tags --sort=-v:refname origin | grep -v 'vrefs/heads/main' | grep -o 'refs/tags/[^^{]*' | sed 's/refs\/tags\///' | head -n 1) short_hash=$(git rev-parse --short HEAD) version=${latest_tag:1} echo "tag_version=${latest_tag}-${short_hash}" | tee -a $GITHUB_OUTPUT echo "version=${version}" | tee -a $GITHUB_OUTPUT echo "commit_ref=$GITHUB_REF_NAME" | tee -a $GITHUB_OUTPUT outputs: tag_version: ${{ steps.version_generation.outputs.tag_version }} version: ${{ steps.version_generation.outputs.version }} commit_ref: ${{ steps.version_generation.outputs.commit_ref }} export_game: runs-on: ubuntu-latest needs: setup name: export game steps: - name: download repo with assets uses: actions/download-artifact@v3 with: path: ./ name: repo-with-assets - name: release requested check run: | if [[ ${{ needs.setup.outputs.commit_ref }} =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]] ; then echo "NEED=true" >> "$GITHUB_ENV" echo 'true' else echo "NEED=false" >> "$GITHUB_ENV" echo 'false' fi - name: update project.godot with new version run: | project_godot_file="${{ github.workspace }}/project.godot" sed -i 's/\(config\/version="\).*\("\)/\1'"${{ needs.setup.outputs.tag_version }}"'\2/' "${project_godot_file}" echo "New project.godot contents:" cat "${project_godot_file}" - name: update release version for Sentry lib run: | sed -i "s/youtd2@[0-9]\+\.[0-9]\+\.[0-9]\+/youtd2@${{ needs.setup.outputs.version }}/g" "${{ github.workspace }}/build/web/full-size.html" - name: create .godot dirs run: | mkdir -p .godot/{editor,imported} - name: export game id: export uses: firebelley/godot-export@v5.2.0 with: godot_executable_download_url: https://github.com/godotengine/godot-builds/releases/download/4.1.3-stable/Godot_v4.1.3-stable_linux.x86_64.zip godot_export_templates_download_url: https://github.com/godotengine/godot-builds/releases/download/4.1.3-stable/Godot_v4.1.3-stable_export_templates.tpz relative_project_path: ./ export_debug: false archive_output: true cache: true env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: repack web artifact run: | zip -uj ${{ steps.export.outputs.archive_directory }}/web.zip ${{ github.workspace }}/build/web/* cp ${{ github.workspace }}/build/web/* ${{ steps.export.outputs.build_directory }}/web/ - uses: actions/upload-artifact@v3 with: name: youtd-${{ needs.setup.outputs.tag_version }} path: ${{ steps.export.outputs.archive_directory }}/*.zip if-no-files-found: error outputs: tag_version: ${{ needs.setup.outputs.tag_version }} commit_ref: ${{ needs.setup.outputs.commit_ref }} release_flag: ${{ env.NEED }} upload_artifacts: needs: export_game strategy: matrix: platform: [windows, macos, web, linux] runs-on: ubuntu-latest steps: - name: download exported files uses: actions/download-artifact@v3 with: name: youtd-${{ needs.export_game.outputs.tag_version }} path: exported_files - name: rename artifacts id: rename_artifacts run: | VERSION_TAG="${{ needs.export_game.outputs.tag_version }}" mv "exported_files/${{ matrix.platform }}.zip" "exported_files/${{ matrix.platform }}-$VERSION_TAG.zip" echo "${{ matrix.platform }}=exported_files/$platform-$VERSION_TAG.zip" >> $GITHUB_OUTPUT - name: upload artifacts id: upload_artifacts uses: actions/upload-artifact@v3 with: name: ${{ matrix.platform }}-${{ needs.export_game.outputs.tag_version }} path: exported_files/${{ matrix.platform }}-${{ needs.export_game.outputs.tag_version }}.zip outputs: macos_artifact: ${{ steps.rename_artifacts.outputs.macos_artifact }} linux_artifact: ${{ steps.rename_artifacts.outputs.linux_artifact }} web_artifact: ${{ steps.rename_artifacts.outputs.web_artifact }} windows_artifact: ${{ steps.rename_artifacts.outputs.windows_artifact }} create_release: name: create release needs: [upload_artifacts, export_game] if: needs.export_game.outputs.release_flag == 'true' runs-on: ubuntu-latest steps: - name: show props run: | echo "Commit ref: $GITHUB_REF_NAME" echo "Release tag: ${{ needs.export_game.outputs.commit_ref }}" - name: download artifacts uses: actions/download-artifact@v3 with: path: . - name: display structure of downloaded files run: ls -R working-directory: . - name: release client uses: ncipollo/release-action@v1.11.2 with: token: ${{ secrets.GITHUB_TOKEN }} tag: ${{ needs.export_game.outputs.commit_ref }} generateReleaseNotes: true artifacts: ./**/*.zip clean_up_artifacts: needs: [create_release, export_game] runs-on: ubuntu-latest steps: - uses: geekyeggo/delete-artifact@v2 with: name: | youtd-${{ needs.export_game.outputs.tag_version }} repo-with-assets ================================================ FILE: .gitignore ================================================ # Godot .gitignore config # # Aims to encompass the most commonly found files that we don't want committed # to Git, such as compilation output, IDE specific files, etc. # # It doesn't cover *all* thirdparty IDE extensions under the sun so if you have # specific needs covered here, you can add them to: # .git/info/exclude # # Or contribute them to this file if they're common enough that a good number of # users would benefit from the shared rules. # # This file is organized by sections, with subsections ordered alphabetically. # - Build configuration # - Godot generated files # - General build output # - IDE and tool specific # - Visual Studio specific # - OS specific ########################### ### Build configuration ### ########################### /custom.py misc/hooks/pre-commit-custom-* ############################# ### Godot generated files ### ############################# # Buildsystem bin/ *.gen.* compile_commands.json platform/windows/godot_res.res # Generated by Godot binary .import/ .godot/ /gdnative_interface.h extension_api.json logs/ *.translation # Generated by unit tests tests/data/*.translation ############################ ### General build output ### ############################ # C/C++ generated *.a *.ax *.d *.dll *.lib *.lo *.o *.os *.ox *.Plo *.so # Binutils tmp linker output of the form "stXXXXXX" where "X" is alphanumeric st[A-Za-z0-9][A-Za-z0-9][A-Za-z0-9][A-Za-z0-9][A-Za-z0-9][A-Za-z0-9] # Python generated __pycache__/ *.pyc # Documentation doc/_build/ # Android .gradle/ local.properties *.iml .gradletasknamecache project.properties platform/android/java/*/.cxx/ platform/android/java/*/build/ platform/android/java/*/libs/ # iOS *.dSYM # Web platform *.bc platform/web/node_modules/ # Misc *.debug ############################# ### IDE and tool specific ### ############################# # Automake .deps/* .dirstamp # ccls .ccls-cache/ # clangd .clangd/ .cache/ # CLion cmake-build-debug # Code::Blocks *.cbp *.layout *.depend # CodeLite *.project *.workspace .codelite/ # Cppcheck *.cppcheck cppcheck-cppcheck-build-dir/ # Eclipse CDT .cproject .settings/ *.pydevproject *.launch # Gcov and Lcov code coverage *.gcno *.gcda *.gcov.html *.func.html *.func-sort-c.html *index-sort-f.html *index-sort-l.html *index.html godot.info amber.png emerald.png glass.png ruby.png snow.png updown.png gcov.css # Geany *.geany .geanyprj # Gprof gmon.out # Jetbrains IDEs .idea/ .fleet/ # Kate *.kate-swp # Kdevelop *.kdev4 # Qt Creator *.config *.creator *.creator.* *.files *.includes *.cflags *.cxxflags # SCons .sconf_temp .sconsign*.dblite .scons_env.json .scons_node_count # Sourcetrail *.srctrl* # Tags # https://github.com/github/gitignore/blob/master/Global/Tags.gitignore # Ignore tags created by etags, ctags, gtags (GNU global) and cscope TAGS !TAGS/ tags *.tags !tags/ gtags.files GTAGS GRTAGS GPATH cscope.files cscope.out cscope.in.out cscope.po.out # Vim *.swo *.swp # Visual Studio Code .vscode/ *.code-workspace .history/ # Xcode xcuserdata/ *.xcscmblueprint *.xccheckout *.xcodeproj/* ############################## ### Visual Studio specific ### ############################## # https://github.com/github/gitignore/blob/master/VisualStudio.gitignore # Ignore Visual Studio temporary files, build results, and # files generated by popular Visual Studio add-ons. # Actual VS project files we don't use *.sln *.vcxproj* # User-specific files *.rsuser *.suo *.user *.userosscache *.sln.docstates # User-specific files (MonoDevelop/Xamarin Studio) *.userprefs # Build results [Dd]ebug/ [Dd]ebugPublic/ [Rr]elease/ [Rr]eleases/ x64/ x86/ [Ww][Ii][Nn]32/ [Aa][Rr][Mm]/ [Aa][Rr][Mm]64/ bld/ [Bb]in/ [Oo]bj/ [Ll]og/ [Ll]ogs/ # Visual Studio 2015/2017 cache/options directory .vs/ # Visual Studio 2017 auto generated files Generated\ Files/ # Files built by Visual Studio *_i.c *_p.c *_h.h *.ilk *.meta *.obj *.iobj *.pch *.pdb *.ipdb *.pgc *.pgd *.rsp *.sbr *.tlb *.tli *.tlh *.tmp *.tmp_proj *_wpftmp.csproj *.log *.tlog *.vspscc *.vssscc .builds *.pidb *.svclog *.scc # Visual C++ cache files ipch/ *.aps *.ncb *.opendb *.opensdf *.sdf *.cachefile *.VC.db *.VC.VC.opendb # Visual Studio profiler *.psess *.vsp *.vspx *.sap # Visual Studio Trace Files *.e2e # ReSharper is a .NET coding add-in _ReSharper*/ *.[Rr]e[Ss]harper *.DotSettings.user # Visual Studio cache files # files ending in .cache can be ignored *.[Cc]ache # Others ClientBin/ enc_temp_folder/ ~$* *.dbmdl *.dbproj.schemaview *.jfm *.pfx *.publishsettings orleans.codegen.cs # Backup & report files from converting an old project file # to a newer Visual Studio version. Backup files are not needed, # because we have git ;-) _UpgradeReport_Files/ Backup*/ UpgradeLog*.XML UpgradeLog*.htm ServiceFabricBackup/ *.rptproj.bak # Hint file for IntelliSense cpp.hint ################### ### OS specific ### ################### # Linux *~ .directory # macOS .DS_Store __MACOSX # Windows # https://github.com/github/gitignore/blob/main/Global/Windows.gitignore [Tt]humbs.db [Tt]humbs.db:encryptable ehthumbs.db ehthumbs_vista.db *.stackdump [Dd]esktop.ini $RECYCLE.BIN/ *.cab *.msi *.msix *.msm *.msp *.lnk ######################## ### Project specific ### ######################## /build/windows/YouTD2.zip /build/linux/YouTD2.zip /build/macos/YouTD2.zip /build/web/* !/build/README.md !/build/web/full-size.html !/build/web/loading_screen.mp4 !/build/web/loading_screen.png override.cfg /webrtc/* !/webrtc/README.md ####################### ### Assets specific ### ####################### # Archives *.7z *.br *.gz *.tar *.zip # Documents *.pdf # Images *.gif *.ico *.jpg *.png *.psd *.webp # Fonts *.woff2 *.otf *.ttf # Audio *.mp3 *.wav *.ogg # Other *.exe /assets/secrets/secrets.csv /assets/secrets/badwords.csv /assets/texts/texts.csv ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2023 Icob2Games Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ # YouTD 2 ![youtd2_logo](https://github.com/user-attachments/assets/ea8a358b-aa12-4d28-9fc5-861bf8d2c497) YouTD 2 is a remake of the tower defense mod "YouTD" from WC3. _[itch.io](https://praytic.itch.io/youtd2)_ _[Discord](https://discord.gg/EksA2CfCS9)_ ## Contributing See [CONTRIBUTING.md](.github/CONTRIBUTING.md). ## Matchmaking server Repository for matchmaking server is [here](https://github.com/Kvel2D/youtd2-server). ## Credits Developed by [Dmitry Degtyarev](https://github.com/Kvel2D), [Praytic](https://github.com/Praytic) and every direct and indirect [contributors](https://github.com/Praytic/youtd2/graphs/contributors) to the GitHub. Based on "YouTD" - a WC3 mod created by geX and the YouTD community. Also thank you to everyone posting feedback and questions on YouTD2 Discord. ## License YouTD 2 source code is licensed under the [MIT licence](https://github.com/Praytic/youtd2/tree/main?tab=MIT-1-ov-file#readme). Assets files are licensed under [CC-BY-NC 4.0](https://creativecommons.org/licenses/by-nc/4.0/legalcode). ================================================ FILE: addons/com.heroiclabs.nakama/Nakama.gd ================================================ @tool extends Node # The default host address of the server. const DEFAULT_HOST : String = "127.0.0.1" # The default port number of the server. const DEFAULT_PORT : int = 7350 # The default timeout for the connections. const DEFAULT_TIMEOUT = 3 # The default protocol scheme for the client connection. const DEFAULT_CLIENT_SCHEME : String = "http" # The default protocol scheme for the socket connection. const DEFAULT_SOCKET_SCHEME : String = "ws" # The default log level for the Nakama logger. const DEFAULT_LOG_LEVEL = NakamaLogger.LOG_LEVEL.DEBUG var _http_adapter = null var logger = NakamaLogger.new() func _ready() -> void: process_mode = Node.PROCESS_MODE_ALWAYS func get_client_adapter() -> NakamaHTTPAdapter: if _http_adapter == null: _http_adapter = NakamaHTTPAdapter.new() _http_adapter.logger = logger _http_adapter.name = "NakamaHTTPAdapter" add_child(_http_adapter) return _http_adapter func create_socket_adapter() -> NakamaSocketAdapter: var adapter = NakamaSocketAdapter.new() adapter.name = "NakamaWebSocketAdapter" adapter.logger = logger add_child(adapter) return adapter func create_client(p_server_key : String, p_host : String = DEFAULT_HOST, p_port : int = DEFAULT_PORT, p_scheme : String = DEFAULT_CLIENT_SCHEME, p_timeout : int = DEFAULT_TIMEOUT, p_log_level : int = DEFAULT_LOG_LEVEL) -> NakamaClient: logger._level = p_log_level return NakamaClient.new(get_client_adapter(), p_server_key, p_scheme, p_host, p_port, p_timeout) func create_socket(p_host : String = DEFAULT_HOST, p_port : int = DEFAULT_PORT, p_scheme : String = DEFAULT_SOCKET_SCHEME) -> NakamaSocket: return NakamaSocket.new(create_socket_adapter(), p_host, p_port, p_scheme, true) func create_socket_from(p_client : NakamaClient) -> NakamaSocket: var scheme = "ws" if p_client.scheme == "https": scheme = "wss" return NakamaSocket.new(create_socket_adapter(), p_client.host, p_client.port, scheme, true) ================================================ FILE: addons/com.heroiclabs.nakama/Satori/NakamaEvent.gd ================================================ extends SatoriAsyncResult # NOTE: renamed from original "Event" to avoid name conflicts class_name NakamaEvent # The name of the event. var name: String # The time when the event was triggered. var timestamp: String # Optional value. var value: String # NakamaEvent metadata, if any. var metadata: Dictionary # Optional event ID assigned by the client, used to de-duplicate in retransmission scenarios. # If not supplied the server will assign a randomly generated unique event identifier. var id: String # The event constructor. # Initializes a new NakamaEvent object. # # @param name The name of the event. # @param timestamp The timestamp of the event. # @param value The value associated with the event (optional). # @param metadata The metadata associated with the event (optional). # @param id The ID of the event (optional). func _init(name: String, timestamp: float, value: String = "", metadata: Dictionary = {}, id: String = "", p_exception = null): super(p_exception) self.name = name self.timestamp = unix_to_protobuf_timestamp_format(timestamp) self.value = value self.metadata = metadata self.id = id func to_api_event_dict() -> Dictionary: return { "name": self.name, "timestamp": self.timestamp, "value": self.value, "metadata": self.metadata, "id": self.id } func unix_to_protobuf_timestamp_format(unix_time: float) -> String: # Extract microseconds precision from unix time var microseconds = int(fmod(unix_time, 1.0) * 1_000_000) # Convert seconds to datetime structure var datetime = Time.get_datetime_dict_from_unix_time(int(unix_time)) var year = datetime.year var month = str(datetime.month).pad_zeros(2) var day = str(datetime.day).pad_zeros(2) var hour = str(datetime.hour).pad_zeros(2) var minute = str(datetime.minute).pad_zeros(2) var second = str(datetime.second).pad_zeros(2) var microsecond = str(microseconds).pad_zeros(6) # Construct the protobuf timestamp format string var timestamp_str = "%s-%s-%sT%s:%s:%s.%sZ" % [year, month, day, hour, minute, second, microsecond] return timestamp_str ================================================ FILE: addons/com.heroiclabs.nakama/Satori/SatoriAPI.gd ================================================ ### Code generated by codegen/main.go. DO NOT EDIT. ### extends RefCounted class_name SatoriAPI # Log out a session, invalidate a refresh token, or log out all sessions/refresh tokens for a user. class ApiAuthenticateLogoutRequest extends SatoriAsyncResult: const _SCHEMA = { "refresh_token": {"name": "_refresh_token", "type": TYPE_STRING, "required": false}, "token": {"name": "_token", "type": TYPE_STRING, "required": false}, } # Refresh token to invalidate. var _refresh_token var refresh_token : String: get: return "" if not _refresh_token is String else String(_refresh_token) # Session token to log out. var _token var token : String: get: return "" if not _token is String else String(_token) func _init(p_exception = null): super(p_exception) static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiAuthenticateLogoutRequest: return _safe_ret(SatoriSerializer.deserialize(p_ns, "ApiAuthenticateLogoutRequest", p_dict), ApiAuthenticateLogoutRequest) as ApiAuthenticateLogoutRequest func serialize() -> Dictionary: return SatoriSerializer.serialize(self) func _to_string() -> String: if is_exception(): return get_exception()._to_string() var output : String = "" var map_string : String = "" output += "refresh_token: %s, " % _refresh_token output += "token: %s, " % _token output += map_string return output # Authenticate against the server with a refresh token. class ApiAuthenticateRefreshRequest extends SatoriAsyncResult: const _SCHEMA = { "refresh_token": {"name": "_refresh_token", "type": TYPE_STRING, "required": false}, } # Refresh token. var _refresh_token var refresh_token : String: get: return "" if not _refresh_token is String else String(_refresh_token) func _init(p_exception = null): super(p_exception) static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiAuthenticateRefreshRequest: return _safe_ret(SatoriSerializer.deserialize(p_ns, "ApiAuthenticateRefreshRequest", p_dict), ApiAuthenticateRefreshRequest) as ApiAuthenticateRefreshRequest func serialize() -> Dictionary: return SatoriSerializer.serialize(self) func _to_string() -> String: if is_exception(): return get_exception()._to_string() var output : String = "" var map_string : String = "" output += "refresh_token: %s, " % _refresh_token output += map_string return output # class ApiAuthenticateRequest extends SatoriAsyncResult: const _SCHEMA = { "custom": {"name": "_custom", "type": TYPE_DICTIONARY, "required": false, "content": TYPE_STRING}, "default": {"name": "_default", "type": TYPE_DICTIONARY, "required": false, "content": TYPE_STRING}, "id": {"name": "_id", "type": TYPE_STRING, "required": false}, } # Optional custom properties to update with this call. # If not set, properties are left as they are on the server. var _custom var custom : Dictionary: get: return Dictionary() if not _custom is Dictionary else _custom.duplicate() # Optional default properties to update with this call. # If not set, properties are left as they are on the server. var _default var default : Dictionary: get: return Dictionary() if not _default is Dictionary else _default.duplicate() # Identity ID. Must be between eight and 128 characters (inclusive). # Must be an alphanumeric string with only underscores and hyphens allowed. var _id var id : String: get: return "" if not _id is String else String(_id) func _init(p_exception = null): super(p_exception) static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiAuthenticateRequest: return _safe_ret(SatoriSerializer.deserialize(p_ns, "ApiAuthenticateRequest", p_dict), ApiAuthenticateRequest) as ApiAuthenticateRequest func serialize() -> Dictionary: return SatoriSerializer.serialize(self) func _to_string() -> String: if is_exception(): return get_exception()._to_string() var output : String = "" var map_string : String = "" if typeof(_custom) == TYPE_DICTIONARY: for k in _custom: map_string += "{%s=%s}, " % [k, _custom[k]] output += "custom: [%s], " % map_string map_string = "" if typeof(_default) == TYPE_DICTIONARY: for k in _default: map_string += "{%s=%s}, " % [k, _default[k]] output += "default: [%s], " % map_string map_string = "" output += "id: %s, " % _id output += map_string return output # A single event. Usually, but not necessarily, part of a batch. class ApiEvent extends SatoriAsyncResult: const _SCHEMA = { "id": {"name": "_id", "type": TYPE_STRING, "required": false}, "metadata": {"name": "_metadata", "type": TYPE_DICTIONARY, "required": false, "content": TYPE_STRING}, "name": {"name": "_name", "type": TYPE_STRING, "required": false}, "timestamp": {"name": "_timestamp", "type": TYPE_STRING, "required": false}, "value": {"name": "_value", "type": TYPE_STRING, "required": false}, } # Optional event ID assigned by the client, used to de-duplicate in retransmission scenarios. # If not supplied the server will assign a randomly generated unique event identifier. var _id var id : String: get: return "" if not _id is String else String(_id) # Event metadata, if any. var _metadata var metadata : Dictionary: get: return Dictionary() if not _metadata is Dictionary else _metadata.duplicate() # Event name. var _name var name : String: get: return "" if not _name is String else String(_name) # The time when the event was triggered on the producer side. var _timestamp var timestamp : String: get: return "" if not _timestamp is String else String(_timestamp) # Optional value. var _value var value : String: get: return "" if not _value is String else String(_value) func _init(p_exception = null): super(p_exception) static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiEvent: return _safe_ret(SatoriSerializer.deserialize(p_ns, "ApiEvent", p_dict), ApiEvent) as ApiEvent func serialize() -> Dictionary: return SatoriSerializer.serialize(self) func _to_string() -> String: if is_exception(): return get_exception()._to_string() var output : String = "" var map_string : String = "" output += "id: %s, " % _id if typeof(_metadata) == TYPE_DICTIONARY: for k in _metadata: map_string += "{%s=%s}, " % [k, _metadata[k]] output += "metadata: [%s], " % map_string map_string = "" output += "name: %s, " % _name output += "timestamp: %s, " % _timestamp output += "value: %s, " % _value output += map_string return output # class ApiEventRequest extends SatoriAsyncResult: const _SCHEMA = { "events": {"name": "_events", "type": TYPE_ARRAY, "required": false, "content": TYPE_DICTIONARY}, } # Some number of events produced by a client. var _events var events : Array: get: return Array() if not _events is Array else Array(_events) func _init(p_exception = null): super(p_exception) static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiEventRequest: return _safe_ret(SatoriSerializer.deserialize(p_ns, "ApiEventRequest", p_dict), ApiEventRequest) as ApiEventRequest func serialize() -> Dictionary: return SatoriSerializer.serialize(self) func _to_string() -> String: if is_exception(): return get_exception()._to_string() var output : String = "" var map_string : String = "" output += "events: %s, " % [_events] output += map_string return output # An experiment that this user is partaking. class ApiExperiment extends SatoriAsyncResult: const _SCHEMA = { "name": {"name": "_name", "type": TYPE_STRING, "required": false}, "value": {"name": "_value", "type": TYPE_STRING, "required": false}, } # var _name var name : String: get: return "" if not _name is String else String(_name) # Value associated with this Experiment. var _value var value : String: get: return "" if not _value is String else String(_value) func _init(p_exception = null): super(p_exception) static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiExperiment: return _safe_ret(SatoriSerializer.deserialize(p_ns, "ApiExperiment", p_dict), ApiExperiment) as ApiExperiment func serialize() -> Dictionary: return SatoriSerializer.serialize(self) func _to_string() -> String: if is_exception(): return get_exception()._to_string() var output : String = "" var map_string : String = "" output += "name: %s, " % _name output += "value: %s, " % _value output += map_string return output # All experiments that this identity is involved with. class ApiExperimentList extends SatoriAsyncResult: const _SCHEMA = { "experiments": {"name": "_experiments", "type": TYPE_ARRAY, "required": false, "content": TYPE_DICTIONARY}, } # All experiments for this identity. var _experiments var experiments : Array: get: return Array() if not _experiments is Array else Array(_experiments) func _init(p_exception = null): super(p_exception) static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiExperimentList: return _safe_ret(SatoriSerializer.deserialize(p_ns, "ApiExperimentList", p_dict), ApiExperimentList) as ApiExperimentList func serialize() -> Dictionary: return SatoriSerializer.serialize(self) func _to_string() -> String: if is_exception(): return get_exception()._to_string() var output : String = "" var map_string : String = "" output += "experiments: %s, " % [_experiments] output += map_string return output # Feature flag available to the identity. class ApiFlag extends SatoriAsyncResult: const _SCHEMA = { "condition_changed": {"name": "_condition_changed", "type": TYPE_BOOL, "required": false}, "name": {"name": "_name", "type": TYPE_STRING, "required": false}, "value": {"name": "_value", "type": TYPE_STRING, "required": false}, } # Whether the value for this flag has conditionally changed from the default state. var _condition_changed var condition_changed : bool: get: return false if not _condition_changed is bool else bool(_condition_changed) # var _name var name : String: get: return "" if not _name is String else String(_name) # Value associated with this flag. var _value var value : String: get: return "" if not _value is String else String(_value) func _init(p_exception = null): super(p_exception) static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiFlag: return _safe_ret(SatoriSerializer.deserialize(p_ns, "ApiFlag", p_dict), ApiFlag) as ApiFlag func serialize() -> Dictionary: return SatoriSerializer.serialize(self) func _to_string() -> String: if is_exception(): return get_exception()._to_string() var output : String = "" var map_string : String = "" output += "condition_changed: %s, " % _condition_changed output += "name: %s, " % _name output += "value: %s, " % _value output += map_string return output # class ApiFlagList extends SatoriAsyncResult: const _SCHEMA = { "flags": {"name": "_flags", "type": TYPE_ARRAY, "required": false, "content": TYPE_DICTIONARY}, } # var _flags var flags : Array: get: return Array() if not _flags is Array else Array(_flags) func _init(p_exception = null): super(p_exception) static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiFlagList: return _safe_ret(SatoriSerializer.deserialize(p_ns, "ApiFlagList", p_dict), ApiFlagList) as ApiFlagList func serialize() -> Dictionary: return SatoriSerializer.serialize(self) func _to_string() -> String: if is_exception(): return get_exception()._to_string() var output : String = "" var map_string : String = "" output += "flags: %s, " % [_flags] output += map_string return output # A response containing all the messages for an identity. class ApiGetMessageListResponse extends SatoriAsyncResult: const _SCHEMA = { "cacheable_cursor": {"name": "_cacheable_cursor", "type": TYPE_STRING, "required": false}, "messages": {"name": "_messages", "type": TYPE_ARRAY, "required": false, "content": TYPE_DICTIONARY}, "next_cursor": {"name": "_next_cursor", "type": TYPE_STRING, "required": false}, "prev_cursor": {"name": "_prev_cursor", "type": TYPE_STRING, "required": false}, } # Cacheable cursor to list newer messages. Durable and designed to be stored, unlike next/prev cursors. var _cacheable_cursor var cacheable_cursor : String: get: return "" if not _cacheable_cursor is String else String(_cacheable_cursor) # The list of messages. var _messages var messages : Array: get: return Array() if not _messages is Array else Array(_messages) # The cursor to send when retrieving the next page, if any. var _next_cursor var next_cursor : String: get: return "" if not _next_cursor is String else String(_next_cursor) # The cursor to send when retrieving the previous page, if any. var _prev_cursor var prev_cursor : String: get: return "" if not _prev_cursor is String else String(_prev_cursor) func _init(p_exception = null): super(p_exception) static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiGetMessageListResponse: return _safe_ret(SatoriSerializer.deserialize(p_ns, "ApiGetMessageListResponse", p_dict), ApiGetMessageListResponse) as ApiGetMessageListResponse func serialize() -> Dictionary: return SatoriSerializer.serialize(self) func _to_string() -> String: if is_exception(): return get_exception()._to_string() var output : String = "" var map_string : String = "" output += "cacheable_cursor: %s, " % _cacheable_cursor output += "messages: %s, " % [_messages] output += "next_cursor: %s, " % _next_cursor output += "prev_cursor: %s, " % _prev_cursor output += map_string return output # Enrich/replace the current session with a new ID. class ApiIdentifyRequest extends SatoriAsyncResult: const _SCHEMA = { "custom": {"name": "_custom", "type": TYPE_DICTIONARY, "required": false, "content": TYPE_STRING}, "default": {"name": "_default", "type": TYPE_DICTIONARY, "required": false, "content": TYPE_STRING}, "id": {"name": "_id", "type": TYPE_STRING, "required": false}, } # Optional custom properties to update with this call. # If not set, properties are left as they are on the server. var _custom var custom : Dictionary: get: return Dictionary() if not _custom is Dictionary else _custom.duplicate() # Optional default properties to update with this call. # If not set, properties are left as they are on the server. var _default var default : Dictionary: get: return Dictionary() if not _default is Dictionary else _default.duplicate() # Identity ID to enrich the current session and return a new session. Old session will no longer be usable. var _id var id : String: get: return "" if not _id is String else String(_id) func _init(p_exception = null): super(p_exception) static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiIdentifyRequest: return _safe_ret(SatoriSerializer.deserialize(p_ns, "ApiIdentifyRequest", p_dict), ApiIdentifyRequest) as ApiIdentifyRequest func serialize() -> Dictionary: return SatoriSerializer.serialize(self) func _to_string() -> String: if is_exception(): return get_exception()._to_string() var output : String = "" var map_string : String = "" if typeof(_custom) == TYPE_DICTIONARY: for k in _custom: map_string += "{%s=%s}, " % [k, _custom[k]] output += "custom: [%s], " % map_string map_string = "" if typeof(_default) == TYPE_DICTIONARY: for k in _default: map_string += "{%s=%s}, " % [k, _default[k]] output += "default: [%s], " % map_string map_string = "" output += "id: %s, " % _id output += map_string return output # A single live event. class ApiLiveEvent extends SatoriAsyncResult: const _SCHEMA = { "active_end_time_sec": {"name": "_active_end_time_sec", "type": TYPE_STRING, "required": false}, "active_start_time_sec": {"name": "_active_start_time_sec", "type": TYPE_STRING, "required": false}, "description": {"name": "_description", "type": TYPE_STRING, "required": false}, "id": {"name": "_id", "type": TYPE_STRING, "required": false}, "name": {"name": "_name", "type": TYPE_STRING, "required": false}, "value": {"name": "_value", "type": TYPE_STRING, "required": false}, } # End time of current event run. var _active_end_time_sec var active_end_time_sec : String: get: return "" if not _active_end_time_sec is String else String(_active_end_time_sec) # Start time of current event run. var _active_start_time_sec var active_start_time_sec : String: get: return "" if not _active_start_time_sec is String else String(_active_start_time_sec) # Description. var _description var description : String: get: return "" if not _description is String else String(_description) # The live event identifier. var _id var id : String: get: return "" if not _id is String else String(_id) # Name. var _name var name : String: get: return "" if not _name is String else String(_name) # Event value. var _value var value : String: get: return "" if not _value is String else String(_value) func _init(p_exception = null): super(p_exception) static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiLiveEvent: return _safe_ret(SatoriSerializer.deserialize(p_ns, "ApiLiveEvent", p_dict), ApiLiveEvent) as ApiLiveEvent func serialize() -> Dictionary: return SatoriSerializer.serialize(self) func _to_string() -> String: if is_exception(): return get_exception()._to_string() var output : String = "" var map_string : String = "" output += "active_end_time_sec: %s, " % _active_end_time_sec output += "active_start_time_sec: %s, " % _active_start_time_sec output += "description: %s, " % _description output += "id: %s, " % _id output += "name: %s, " % _name output += "value: %s, " % _value output += map_string return output # List of Live events. class ApiLiveEventList extends SatoriAsyncResult: const _SCHEMA = { "live_events": {"name": "_live_events", "type": TYPE_ARRAY, "required": false, "content": TYPE_DICTIONARY}, } # Live events. var _live_events var live_events : Array: get: return Array() if not _live_events is Array else Array(_live_events) func _init(p_exception = null): super(p_exception) static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiLiveEventList: return _safe_ret(SatoriSerializer.deserialize(p_ns, "ApiLiveEventList", p_dict), ApiLiveEventList) as ApiLiveEventList func serialize() -> Dictionary: return SatoriSerializer.serialize(self) func _to_string() -> String: if is_exception(): return get_exception()._to_string() var output : String = "" var map_string : String = "" output += "live_events: %s, " % [_live_events] output += map_string return output # A scheduled message. class ApiMessage extends SatoriAsyncResult: const _SCHEMA = { "consume_time": {"name": "_consume_time", "type": TYPE_STRING, "required": false}, "create_time": {"name": "_create_time", "type": TYPE_STRING, "required": false}, "metadata": {"name": "_metadata", "type": TYPE_DICTIONARY, "required": false, "content": TYPE_STRING}, "read_time": {"name": "_read_time", "type": TYPE_STRING, "required": false}, "schedule_id": {"name": "_schedule_id", "type": TYPE_STRING, "required": false}, "send_time": {"name": "_send_time", "type": TYPE_STRING, "required": false}, "text": {"name": "_text", "type": TYPE_STRING, "required": false}, "update_time": {"name": "_update_time", "type": TYPE_STRING, "required": false}, } # The time the message was consumed by the identity. var _consume_time var consume_time : String: get: return "" if not _consume_time is String else String(_consume_time) # The time the message was created. var _create_time var create_time : String: get: return "" if not _create_time is String else String(_create_time) # A key-value pairs of metadata. var _metadata var metadata : Dictionary: get: return Dictionary() if not _metadata is Dictionary else _metadata.duplicate() # The time the message was read by the client. var _read_time var read_time : String: get: return "" if not _read_time is String else String(_read_time) # The identifier of the schedule. var _schedule_id var schedule_id : String: get: return "" if not _schedule_id is String else String(_schedule_id) # The send time for the message. var _send_time var send_time : String: get: return "" if not _send_time is String else String(_send_time) # The message's text. var _text var text : String: get: return "" if not _text is String else String(_text) # The time the message was updated. var _update_time var update_time : String: get: return "" if not _update_time is String else String(_update_time) func _init(p_exception = null): super(p_exception) static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiMessage: return _safe_ret(SatoriSerializer.deserialize(p_ns, "ApiMessage", p_dict), ApiMessage) as ApiMessage func serialize() -> Dictionary: return SatoriSerializer.serialize(self) func _to_string() -> String: if is_exception(): return get_exception()._to_string() var output : String = "" var map_string : String = "" output += "consume_time: %s, " % _consume_time output += "create_time: %s, " % _create_time if typeof(_metadata) == TYPE_DICTIONARY: for k in _metadata: map_string += "{%s=%s}, " % [k, _metadata[k]] output += "metadata: [%s], " % map_string map_string = "" output += "read_time: %s, " % _read_time output += "schedule_id: %s, " % _schedule_id output += "send_time: %s, " % _send_time output += "text: %s, " % _text output += "update_time: %s, " % _update_time output += map_string return output # Properties associated with an identity. class ApiProperties extends SatoriAsyncResult: const _SCHEMA = { "computed": {"name": "_computed", "type": TYPE_DICTIONARY, "required": false, "content": TYPE_STRING}, "custom": {"name": "_custom", "type": TYPE_DICTIONARY, "required": false, "content": TYPE_STRING}, "default": {"name": "_default", "type": TYPE_DICTIONARY, "required": false, "content": TYPE_STRING}, } # Event computed properties. var _computed var computed : Dictionary: get: return Dictionary() if not _computed is Dictionary else _computed.duplicate() # Event custom properties. var _custom var custom : Dictionary: get: return Dictionary() if not _custom is Dictionary else _custom.duplicate() # Event default properties. var _default var default : Dictionary: get: return Dictionary() if not _default is Dictionary else _default.duplicate() func _init(p_exception = null): super(p_exception) static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiProperties: return _safe_ret(SatoriSerializer.deserialize(p_ns, "ApiProperties", p_dict), ApiProperties) as ApiProperties func serialize() -> Dictionary: return SatoriSerializer.serialize(self) func _to_string() -> String: if is_exception(): return get_exception()._to_string() var output : String = "" var map_string : String = "" if typeof(_computed) == TYPE_DICTIONARY: for k in _computed: map_string += "{%s=%s}, " % [k, _computed[k]] output += "computed: [%s], " % map_string map_string = "" if typeof(_custom) == TYPE_DICTIONARY: for k in _custom: map_string += "{%s=%s}, " % [k, _custom[k]] output += "custom: [%s], " % map_string map_string = "" if typeof(_default) == TYPE_DICTIONARY: for k in _default: map_string += "{%s=%s}, " % [k, _default[k]] output += "default: [%s], " % map_string map_string = "" output += map_string return output # A session. class ApiSession extends SatoriAsyncResult: const _SCHEMA = { "properties": {"name": "_properties", "type": "ApiProperties", "required": false}, "refresh_token": {"name": "_refresh_token", "type": TYPE_STRING, "required": false}, "token": {"name": "_token", "type": TYPE_STRING, "required": false}, } # Properties associated with this identity. var _properties var properties : ApiProperties: get: return _properties as ApiProperties # Refresh token. var _refresh_token var refresh_token : String: get: return "" if not _refresh_token is String else String(_refresh_token) # Token credential. var _token var token : String: get: return "" if not _token is String else String(_token) func _init(p_exception = null): super(p_exception) static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiSession: return _safe_ret(SatoriSerializer.deserialize(p_ns, "ApiSession", p_dict), ApiSession) as ApiSession func serialize() -> Dictionary: return SatoriSerializer.serialize(self) func _to_string() -> String: if is_exception(): return get_exception()._to_string() var output : String = "" var map_string : String = "" output += "properties: %s, " % _properties output += "refresh_token: %s, " % _refresh_token output += "token: %s, " % _token output += map_string return output # Update Properties associated with this identity. class ApiUpdatePropertiesRequest extends SatoriAsyncResult: const _SCHEMA = { "custom": {"name": "_custom", "type": TYPE_DICTIONARY, "required": false, "content": TYPE_STRING}, "default": {"name": "_default", "type": TYPE_DICTIONARY, "required": false, "content": TYPE_STRING}, "recompute": {"name": "_recompute", "type": TYPE_BOOL, "required": false}, } # Event custom properties. var _custom var custom : Dictionary: get: return Dictionary() if not _custom is Dictionary else _custom.duplicate() # Event default properties. var _default var default : Dictionary: get: return Dictionary() if not _default is Dictionary else _default.duplicate() # Informs the server to recompute the audience membership of the identity. var _recompute var recompute : bool: get: return false if not _recompute is bool else bool(_recompute) func _init(p_exception = null): super(p_exception) static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiUpdatePropertiesRequest: return _safe_ret(SatoriSerializer.deserialize(p_ns, "ApiUpdatePropertiesRequest", p_dict), ApiUpdatePropertiesRequest) as ApiUpdatePropertiesRequest func serialize() -> Dictionary: return SatoriSerializer.serialize(self) func _to_string() -> String: if is_exception(): return get_exception()._to_string() var output : String = "" var map_string : String = "" if typeof(_custom) == TYPE_DICTIONARY: for k in _custom: map_string += "{%s=%s}, " % [k, _custom[k]] output += "custom: [%s], " % map_string map_string = "" if typeof(_default) == TYPE_DICTIONARY: for k in _default: map_string += "{%s=%s}, " % [k, _default[k]] output += "default: [%s], " % map_string map_string = "" output += "recompute: %s, " % _recompute output += map_string return output # class ProtobufAny extends SatoriAsyncResult: const _SCHEMA = { "type": {"name": "_type", "type": TYPE_STRING, "required": false}, } # var _type var type : String: get: return "" if not _type is String else String(_type) func _init(p_exception = null): super(p_exception) static func create(p_ns : GDScript, p_dict : Dictionary) -> ProtobufAny: return _safe_ret(SatoriSerializer.deserialize(p_ns, "ProtobufAny", p_dict), ProtobufAny) as ProtobufAny func serialize() -> Dictionary: return SatoriSerializer.serialize(self) func _to_string() -> String: if is_exception(): return get_exception()._to_string() var output : String = "" var map_string : String = "" output += "type: %s, " % _type output += map_string return output # class RpcStatus extends SatoriAsyncResult: const _SCHEMA = { "code": {"name": "_code", "type": TYPE_INT, "required": false}, "details": {"name": "_details", "type": TYPE_ARRAY, "required": false, "content": TYPE_DICTIONARY}, "message": {"name": "_message", "type": TYPE_STRING, "required": false}, } # var _code var code : int: get: return 0 if not _code is int else int(_code) # var _details var details : Array: get: return Array() if not _details is Array else Array(_details) # var _message var message : String: get: return "" if not _message is String else String(_message) func _init(p_exception = null): super(p_exception) static func create(p_ns : GDScript, p_dict : Dictionary) -> RpcStatus: return _safe_ret(SatoriSerializer.deserialize(p_ns, "RpcStatus", p_dict), RpcStatus) as RpcStatus func serialize() -> Dictionary: return SatoriSerializer.serialize(self) func _to_string() -> String: if is_exception(): return get_exception()._to_string() var output : String = "" var map_string : String = "" output += "code: %s, " % _code output += "details: %s, " % [_details] output += "message: %s, " % _message output += map_string return output # The low level client for the Satori API. class ApiClient extends RefCounted: var _base_uri : String var _http_adapter var _namespace : GDScript var _server_key : String var auto_refresh := true var auto_refresh_time := 300 var auto_retry : bool: set(p_value): _http_adapter.auto_retry = p_value get: return _http_adapter.auto_retry var auto_retry_count : int: set(p_value): _http_adapter.auto_retry_count = p_value get: return _http_adapter.auto_retry_count var auto_retry_backoff_base : int: set(p_value): _http_adapter.auto_retry_backoff_base = p_value get: return _http_adapter.auto_retry_backoff_base var last_cancel_token: get: return _http_adapter.get_last_token() func _init(p_base_uri : String, p_http_adapter, p_namespace : GDScript, p_server_key : String, p_timeout : int = 10): _base_uri = p_base_uri _http_adapter = p_http_adapter _http_adapter.timeout = p_timeout _namespace = p_namespace _server_key = p_server_key func _refresh_session(p_session : SatoriSession): if auto_refresh and p_session.is_valid() and p_session.refresh_token and not p_session.is_refresh_expired() and p_session.would_expire_in(auto_refresh_time): var request = ApiAuthenticateRefreshRequest.new() request._token = p_session.refresh_token return await authenticate_refresh_async(_server_key, "", request) return null func cancel_request(p_token): if p_token: _http_adapter.cancel_request(p_token) # A healthcheck which load balancers can use to check the service. func healthcheck_async( p_session : SatoriSession ) -> SatoriAsyncResult: var try_refresh = await _refresh_session(p_session) if try_refresh != null: if try_refresh.is_exception(): return SatoriAsyncResult.new(try_refresh.get_exception()) await p_session.refresh(try_refresh) var urlpath : String = "/healthcheck" var query_params = "" var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""] var method = "GET" var headers = {} var header = "Bearer %s" % p_session.token headers["Authorization"] = header var content : PackedByteArray = PackedByteArray() var result = await _http_adapter.send_async(method, uri, headers, content) if result is SatoriException: return SatoriAsyncResult.new(result) return SatoriAsyncResult.new() # A readycheck which load balancers can use to check the service. func readycheck_async( p_session : SatoriSession ) -> SatoriAsyncResult: var try_refresh = await _refresh_session(p_session) if try_refresh != null: if try_refresh.is_exception(): return SatoriAsyncResult.new(try_refresh.get_exception()) await p_session.refresh(try_refresh) var urlpath : String = "/readycheck" var query_params = "" var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""] var method = "GET" var headers = {} var header = "Bearer %s" % p_session.token headers["Authorization"] = header var content : PackedByteArray = PackedByteArray() var result = await _http_adapter.send_async(method, uri, headers, content) if result is SatoriException: return SatoriAsyncResult.new(result) return SatoriAsyncResult.new() # Authenticate against the server. func authenticate_async( p_basic_auth_username : String , p_basic_auth_password : String , p_body : ApiAuthenticateRequest ) -> ApiSession: var urlpath : String = "/v1/authenticate" var query_params = "" var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""] var method = "POST" var headers = {} var credentials = Marshalls.utf8_to_base64(p_basic_auth_username + ":" + p_basic_auth_password) var header = "Basic %s" % credentials headers["Authorization"] = header var content : PackedByteArray = PackedByteArray() content = JSON.stringify(p_body.serialize()).to_utf8_buffer() var result = await _http_adapter.send_async(method, uri, headers, content) if result is SatoriException: return ApiSession.new(result) var out : ApiSession = SatoriSerializer.deserialize(_namespace, "ApiSession", result) return out # Log out a session, invalidate a refresh token, or log out all sessions/refresh tokens for a user. func authenticate_logout_async( p_session : SatoriSession , p_body : ApiAuthenticateLogoutRequest ) -> SatoriAsyncResult: var try_refresh = await _refresh_session(p_session) if try_refresh != null: if try_refresh.is_exception(): return SatoriAsyncResult.new(try_refresh.get_exception()) await p_session.refresh(try_refresh) var urlpath : String = "/v1/authenticate/logout" var query_params = "" var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""] var method = "POST" var headers = {} var header = "Bearer %s" % p_session.token headers["Authorization"] = header var content : PackedByteArray = PackedByteArray() content = JSON.stringify(p_body.serialize()).to_utf8_buffer() var result = await _http_adapter.send_async(method, uri, headers, content) if result is SatoriException: return SatoriAsyncResult.new(result) return SatoriAsyncResult.new() # Refresh a user's session using a refresh token retrieved from a previous authentication request. func authenticate_refresh_async( p_basic_auth_username : String , p_basic_auth_password : String , p_body : ApiAuthenticateRefreshRequest ) -> ApiSession: var urlpath : String = "/v1/authenticate/refresh" var query_params = "" var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""] var method = "POST" var headers = {} var credentials = Marshalls.utf8_to_base64(p_basic_auth_username + ":" + p_basic_auth_password) var header = "Basic %s" % credentials headers["Authorization"] = header var content : PackedByteArray = PackedByteArray() content = JSON.stringify(p_body.serialize()).to_utf8_buffer() var result = await _http_adapter.send_async(method, uri, headers, content) if result is SatoriException: return ApiSession.new(result) var out : ApiSession = SatoriSerializer.deserialize(_namespace, "ApiSession", result) return out # Publish an event for this session. func event_async( p_session : SatoriSession , p_body : ApiEventRequest ) -> SatoriAsyncResult: var try_refresh = await _refresh_session(p_session) if try_refresh != null: if try_refresh.is_exception(): return SatoriAsyncResult.new(try_refresh.get_exception()) await p_session.refresh(try_refresh) var urlpath : String = "/v1/event" var query_params = "" var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""] var method = "POST" var headers = {} var header = "Bearer %s" % p_session.token headers["Authorization"] = header var content : PackedByteArray = PackedByteArray() content = JSON.stringify(p_body.serialize()).to_utf8_buffer() var result = await _http_adapter.send_async(method, uri, headers, content) if result is SatoriException: return SatoriAsyncResult.new(result) return SatoriAsyncResult.new() # Get or list all available experiments for this identity. func get_experiments_async( p_session : SatoriSession , p_names = null # : array ) -> ApiExperimentList: var try_refresh = await _refresh_session(p_session) if try_refresh != null: if try_refresh.is_exception(): return ApiExperimentList.new(try_refresh.get_exception()) await p_session.refresh(try_refresh) var urlpath : String = "/v1/experiment" var query_params = "" if p_names != null: for elem in p_names: query_params += "names=%s&" % elem var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""] var method = "GET" var headers = {} var header = "Bearer %s" % p_session.token headers["Authorization"] = header var content : PackedByteArray = PackedByteArray() var result = await _http_adapter.send_async(method, uri, headers, content) if result is SatoriException: return ApiExperimentList.new(result) var out : ApiExperimentList = SatoriSerializer.deserialize(_namespace, "ApiExperimentList", result) return out # List all available flags for this identity. func get_flags_async( p_bearer_token : String , p_names = null # : array ) -> ApiFlagList: var urlpath : String = "/v1/flag" var query_params = "" if p_names != null: for elem in p_names: query_params += "names=%s&" % elem var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""] var method = "GET" var headers = {} if (p_bearer_token): var header = "Bearer %s" % p_bearer_token headers["Authorization"] = header var content : PackedByteArray = PackedByteArray() var result = await _http_adapter.send_async(method, uri, headers, content) if result is SatoriException: return ApiFlagList.new(result) var out : ApiFlagList = SatoriSerializer.deserialize(_namespace, "ApiFlagList", result) return out # Enrich/replace the current session with new identifier. func identify_async( p_session : SatoriSession , p_body : ApiIdentifyRequest ) -> ApiSession: var try_refresh = await _refresh_session(p_session) if try_refresh != null: if try_refresh.is_exception(): return ApiSession.new(try_refresh.get_exception()) await p_session.refresh(try_refresh) var urlpath : String = "/v1/identify" var query_params = "" var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""] var method = "PUT" var headers = {} var header = "Bearer %s" % p_session.token headers["Authorization"] = header var content : PackedByteArray = PackedByteArray() content = JSON.stringify(p_body.serialize()).to_utf8_buffer() var result = await _http_adapter.send_async(method, uri, headers, content) if result is SatoriException: return ApiSession.new(result) var out : ApiSession = SatoriSerializer.deserialize(_namespace, "ApiSession", result) return out # Delete the caller's identity and associated data. func delete_identity_async( p_session : SatoriSession ) -> SatoriAsyncResult: var try_refresh = await _refresh_session(p_session) if try_refresh != null: if try_refresh.is_exception(): return SatoriAsyncResult.new(try_refresh.get_exception()) await p_session.refresh(try_refresh) var urlpath : String = "/v1/identity" var query_params = "" var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""] var method = "DELETE" var headers = {} var header = "Bearer %s" % p_session.token headers["Authorization"] = header var content : PackedByteArray = PackedByteArray() var result = await _http_adapter.send_async(method, uri, headers, content) if result is SatoriException: return SatoriAsyncResult.new(result) return SatoriAsyncResult.new() # List available live events. func get_live_events_async( p_session : SatoriSession , p_names = null # : array ) -> ApiLiveEventList: var try_refresh = await _refresh_session(p_session) if try_refresh != null: if try_refresh.is_exception(): return ApiLiveEventList.new(try_refresh.get_exception()) await p_session.refresh(try_refresh) var urlpath : String = "/v1/live-event" var query_params = "" if p_names != null: for elem in p_names: query_params += "names=%s&" % elem var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""] var method = "GET" var headers = {} var header = "Bearer %s" % p_session.token headers["Authorization"] = header var content : PackedByteArray = PackedByteArray() var result = await _http_adapter.send_async(method, uri, headers, content) if result is SatoriException: return ApiLiveEventList.new(result) var out : ApiLiveEventList = SatoriSerializer.deserialize(_namespace, "ApiLiveEventList", result) return out # Get the list of messages for the identity. func get_message_list_async( p_session : SatoriSession , p_limit = null # : integer , p_forward = null # : boolean , p_cursor = null # : string ) -> ApiGetMessageListResponse: var try_refresh = await _refresh_session(p_session) if try_refresh != null: if try_refresh.is_exception(): return ApiGetMessageListResponse.new(try_refresh.get_exception()) await p_session.refresh(try_refresh) var urlpath : String = "/v1/message" var query_params = "" if p_limit != null: query_params += "limit=%d&" % p_limit if p_forward != null: query_params += "forward=%s&" % str(bool(p_forward)).to_lower() if p_cursor != null: query_params += "cursor=%s&" % SatoriSerializer.escape_http(p_cursor) var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""] var method = "GET" var headers = {} var header = "Bearer %s" % p_session.token headers["Authorization"] = header var content : PackedByteArray = PackedByteArray() var result = await _http_adapter.send_async(method, uri, headers, content) if result is SatoriException: return ApiGetMessageListResponse.new(result) var out : ApiGetMessageListResponse = SatoriSerializer.deserialize(_namespace, "ApiGetMessageListResponse", result) return out # Deletes a message for an identity. func delete_message_async( p_session : SatoriSession , p_id : String ) -> SatoriAsyncResult: var try_refresh = await _refresh_session(p_session) if try_refresh != null: if try_refresh.is_exception(): return SatoriAsyncResult.new(try_refresh.get_exception()) await p_session.refresh(try_refresh) var urlpath : String = "/v1/message/{id}" urlpath = urlpath.replace("{id}", SatoriSerializer.escape_http(p_id)) var query_params = "" var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""] var method = "DELETE" var headers = {} var header = "Bearer %s" % p_session.token headers["Authorization"] = header var content : PackedByteArray = PackedByteArray() var result = await _http_adapter.send_async(method, uri, headers, content) if result is SatoriException: return SatoriAsyncResult.new(result) return SatoriAsyncResult.new() # Updates a message for an identity. func update_message_async( p_session : SatoriSession , p_id : String , p_body : ) -> SatoriAsyncResult: var try_refresh = await _refresh_session(p_session) if try_refresh != null: if try_refresh.is_exception(): return SatoriAsyncResult.new(try_refresh.get_exception()) await p_session.refresh(try_refresh) var urlpath : String = "/v1/message/{id}" urlpath = urlpath.replace("{id}", SatoriSerializer.escape_http(p_id)) var query_params = "" var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""] var method = "PUT" var headers = {} var header = "Bearer %s" % p_session.token headers["Authorization"] = header var content : PackedByteArray = PackedByteArray() content = JSON.stringify(p_body.serialize()).to_utf8_buffer() var result = await _http_adapter.send_async(method, uri, headers, content) if result is SatoriException: return SatoriAsyncResult.new(result) return SatoriAsyncResult.new() # List properties associated with this identity. func list_properties_async( p_session : SatoriSession ) -> ApiProperties: var try_refresh = await _refresh_session(p_session) if try_refresh != null: if try_refresh.is_exception(): return ApiProperties.new(try_refresh.get_exception()) await p_session.refresh(try_refresh) var urlpath : String = "/v1/properties" var query_params = "" var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""] var method = "GET" var headers = {} var header = "Bearer %s" % p_session.token headers["Authorization"] = header var content : PackedByteArray = PackedByteArray() var result = await _http_adapter.send_async(method, uri, headers, content) if result is SatoriException: return ApiProperties.new(result) var out : ApiProperties = SatoriSerializer.deserialize(_namespace, "ApiProperties", result) return out # Update identity properties. func update_properties_async( p_session : SatoriSession , p_body : ApiUpdatePropertiesRequest ) -> SatoriAsyncResult: var try_refresh = await _refresh_session(p_session) if try_refresh != null: if try_refresh.is_exception(): return SatoriAsyncResult.new(try_refresh.get_exception()) await p_session.refresh(try_refresh) var urlpath : String = "/v1/properties" var query_params = "" var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""] var method = "PUT" var headers = {} var header = "Bearer %s" % p_session.token headers["Authorization"] = header var content : PackedByteArray = PackedByteArray() content = JSON.stringify(p_body.serialize()).to_utf8_buffer() var result = await _http_adapter.send_async(method, uri, headers, content) if result is SatoriException: return SatoriAsyncResult.new(result) return SatoriAsyncResult.new() ================================================ FILE: addons/com.heroiclabs.nakama/Satori/SatoriClient.gd ================================================ extends RefCounted ## A client for the API in Satori Server. class_name SatoriClient #region Properties var _host ## The host address of the server. var host : String: get: return _host var _port ## The port number of the server. var port : int: get: return _port var _scheme ## The protocol scheme used to connect with the server. Must be either "http" or "https". var scheme : String: get: return _scheme ## The key used to authenticate with the server without a session. var api_key : String ## Set the timeout in seconds on requests sent to the server. var timeout : int var _api_client : SatoriAPI.ApiClient var auto_refresh : bool = false: set(v): set_auto_refresh(v) get: return get_auto_refresh() func get_auto_refresh(): return _api_client.auto_refresh func set_auto_refresh(p_value): _api_client.auto_refresh = p_value #endregion #region Initialization func _init(p_adapter : SatoriHTTPAdapter, p_api_key : String, p_scheme : String, p_host : String, p_port : int, p_timeout : int): api_key = p_api_key _scheme = p_scheme _host = p_host _port = p_port timeout = p_timeout _api_client = SatoriAPI.ApiClient.new(_scheme + "://" + _host + ":" + str(_port), p_adapter, SatoriAPI, api_key, p_timeout) #endregion #region Client APIs ## Authenticate against the server. ## [p_id]: An optional user id. ## [p_default_properties]: Optional default properties to update with this call. ## If not set, properties are left as they are on the server. ## [p_custom_properties]: Optional custom properties to update with this call. ## If not set, properties are left as they are on the server. func authenticate_async(p_id: String, p_default_properties: Dictionary = {}, p_custom_properties: Dictionary = {}) -> SatoriSession: return _parse_session(await _api_client.authenticate_async(api_key, "", SatoriAPI.ApiAuthenticateRequest.create(SatoriAPI, { "id": p_id, "default": p_default_properties, "custom": p_custom_properties }))) ## Log out a session, invalidate a refresh token, or log out all sessions/refresh tokens for a user. ## [p_session]: The session of the user. func authenticate_logout_async(p_session: SatoriSession) -> SatoriAsyncResult: return await _api_client.authenticate_logout_async(p_session, SatoriAPI.ApiAuthenticateLogoutRequest.create(SatoriAPI, { "refresh_token": p_session.refresh_token, "token": p_session.token })) # Parses the Satori API session and returns a SatoriSession object. func _parse_session(p_session: SatoriAPI.ApiSession) -> SatoriSession: if p_session.is_exception(): return SatoriSession.new(null, null, p_session.get_exception()) return SatoriSession.new(p_session.token, p_session.refresh_token) ## Refresh a user's session using a refresh token retrieved from a previous authentication request. ## [p_sesison]: The session of the user. func session_refresh_async(p_session : SatoriSession) -> SatoriSession: return _parse_session(await _api_client.authenticate_refresh_async(api_key, "", SatoriAPI.ApiAuthenticateRefreshRequest.create(SatoriAPI, { "refresh_token": p_session.refresh_token, }) )) ## Send an event for this session. ## [p_session]: The session of the user. ## [p_event]: The event which will be sent. func event_async(p_session: SatoriSession, p_event: NakamaEvent) -> SatoriAsyncResult: return await events_async(p_session, [ p_event ]) ## Send a batch of events for this session. ## [p_session]: The session of the user. ## [p_events]: The batch of events which will be sent. func events_async(p_session: SatoriSession, p_events: Array) -> SatoriAsyncResult: var p_dict = { "events": p_events.map(func(e): return e.to_api_event_dict()) } var req = SatoriAPI.ApiEventRequest.create(SatoriAPI, p_dict) return await _api_client.event_async(p_session, req) ## Get all experiments data. ## [p_session]: The session of the user. func get_all_experiments_async(p_session: SatoriSession) -> SatoriAsyncResult: return await _api_client.get_experiments_async(p_session) ## Get specific experiments data. ## [p_session]: The session of the user. ## [p_names]: Experiment names. func get_experiments_async(p_session: SatoriSession, p_names: Array) -> SatoriAPI.ApiExperimentList: return await _api_client.get_experiments_async(p_session, p_names) ## Get a single flag for this identity. ## This method will return the default value ## specified and will not raise an exception if the network is unavailable ## [p_session]: The session of the user. ## [p_name]: The name of the flag. ## [p_default]: The default value if the server is unreachable. func get_flag_async(p_session: SatoriSession, p_name: String, p_default: String = "") -> SatoriAPI.ApiFlag: var p_names = [p_name] var flags = await get_flags_async(p_session, p_names) if flags.is_exception(): return SatoriAPI.ApiFlag.create(SatoriAPI, { "name": p_name, "value": p_default }) for flag in flags.flags: if flag.name == p_name: return flag return null ## List all available flags for this identity. ## [p_session]: The session of the user. ## [p_names]: Flag names, if empty all flags will be returned. func get_flags_async(p_session: SatoriSession, p_names: Array) -> SatoriAPI.ApiFlagList: return await _api_client.get_flags_async(p_session.token, p_names) ## List available live events. ## [p_session]: The session of the user. ## [p_names]: Live event names, if null or empty all live events are returned. func get_live_events_async(p_session: SatoriSession, p_names: Array = []) -> SatoriAPI.ApiLiveEventList: return await _api_client.get_live_events_async(p_session, p_names) ## Identify a session with a new ID. ## [p_session]: The session of the user. ## [p_id]: Identity ID to enrich the current session and return a new session. ## The old session will no longer be usable. ## Must be between eight and 128 characters (inclusive). ## Must be an alphanumeric string with only underscores and hyphens allowed. ## [p_default_properties]: The default properties. ## [p_custom_properties]: The custom event properties. func identify_async(p_session: SatoriSession, p_id: String, p_default_properties: Dictionary = {}, p_custom_properties: Dictionary = {}) -> SatoriSession: var req = SatoriAPI.ApiIdentifyRequest.create(SatoriAPI, { "id": p_id, "default": p_default_properties, "custom": p_custom_properties }) return _parse_session(await _api_client.identify_async(p_session, req)) ## List properties associated with this identity. ## [p_session]: The session of the user. func list_properties_async(p_session: SatoriSession) -> SatoriAsyncResult: return await _api_client.list_properties_async(p_session) ## Update properties associated with this identity. ## [p_session]: The session of the user. ## [p_default_properties]: The default properties to update. ## [p_custom_properties]: The custom properties to update. ## [p_recompute]: Whether or not to recompute the user's audience membership immediately after property update. func update_properties_async(p_session: SatoriSession, p_default_properties: Dictionary, p_custom_properties: Dictionary, p_recompute: bool = false) -> SatoriAsyncResult: var req = SatoriAPI.ApiUpdatePropertiesRequest.create(SatoriAPI, { "default": p_default_properties, "custom": p_custom_properties, "recompute": p_recompute }) return await _api_client.update_properties_async(p_session, req) ## Delete the caller's identity and associated data. ## [p_session]: The session of the user. func delete_identity_async(p_session: SatoriSession) -> SatoriAsyncResult: return await _api_client.delete_identity_async(p_session) #endregion ================================================ FILE: addons/com.heroiclabs.nakama/Satori/SatoriHttpAdapter.gd ================================================ @tool extends Node # An adapter which implements the HTTP protocol. class_name SatoriHTTPAdapter # The logger to use with the adapter. var logger : RefCounted = SatoriLogger.new() # The timeout for requests var timeout : int = 3 # If request should be automatically retried when a network error occurs. var auto_retry : bool = true # The maximum number of time a request will be retried when auto_retry is true var auto_retry_count : int = 3 var auto_retry_backoff_base : int = 10 # Whether or not to use threads when making HTTP requests. var use_threads : bool = true var _pending = {} var id : int = 0 class AsyncRequest: var id : int var request : HTTPRequest var uri : String var method : int var headers : PackedStringArray var body : PackedByteArray var retry_count := 3 var backoff_time := 10 var logger : SatoriLogger var cancelled = false var result : int = HTTPRequest.RESULT_NO_RESPONSE var response_code : int = -1 var response_body : PackedByteArray var timer : SceneTreeTimer = null var cur_try : int = 1 var rng = RandomNumberGenerator.new() func _init(p_id : int, p_request : HTTPRequest, p_uri : String, p_method : int, p_headers : PackedStringArray, p_body : PackedByteArray, p_retry_count : int, p_backoff_time : int, p_logger : SatoriLogger): rng.seed = Time.get_ticks_usec() id = p_id request = p_request uri = p_uri method = p_method headers = p_headers body = p_body retry_count = p_retry_count backoff_time = p_backoff_time logger = p_logger func should_retry(): return cur_try < retry_count and not cancelled func retry(): var time = pow(backoff_time, cur_try) * rng.randf_range(0.5, 1) logger.debug("Retrying request %d. Tries left: %d. Backoff: %d ms" % [ id, retry_count - cur_try, time ]) cur_try += 1 await backoff(time) if cancelled: return return await make_request() func make_request(): var err = request.request(uri, headers, method, body.get_string_from_utf8()) if err != OK: await request.get_tree().process_frame result = HTTPRequest.RESULT_CANT_CONNECT logger.debug("Request %d failed to start, error: %d" % [id, err]) return var args = await request.request_completed result = args[0] response_code = args[1] response_body = args[3] func backoff(p_time : int): timer = request.get_tree().create_timer(p_time / 1000.0) await timer.timeout timer = null func cancel(): cancelled = true request.cancel_request() if timer: timer.time_left = 0 else: request.call_deferred("emit_signal", "request_completed", HTTPRequest.RESULT_REQUEST_FAILED, 0, [], []) func parse_result(): if cancelled: return SatoriException.new("Request cancelled", -1, -1, true) elif result != HTTPRequest.RESULT_SUCCESS: if result == null: result = 0 return SatoriException.new("HTTPRequest failed!", result) var json = JSON.new() var json_error = json.parse(response_body.get_string_from_utf8()) if json_error != OK: logger.debug("Unable to parse request %d response. JSON error: %d, JSON error message: %s, response code: %d" % [ id, json_error, json.get_error_message(), response_code ]) return SatoriException.new("Failed to decode JSON response", response_code) var parsed = json.get_data() if response_code != HTTPClient.RESPONSE_OK: var error = "" var code = -1 if typeof(parsed) == TYPE_DICTIONARY: if "message" in parsed: error = parsed["message"] elif "error" in parsed: error = parsed["error"] else: error = str(parsed) code = parsed["code"] if "code" in parsed else -1 else: error = str(parsed) if typeof(error) == TYPE_DICTIONARY: error = JSON.stringify(error) logger.debug("Request %d returned response code: %d, RPC code: %d, error: %s" % [ id, response_code, code, error ]) return SatoriException.new(error, response_code, code) return parsed # Send a HTTP request. # @param method - HTTP method to use for this request. # @param uri - The fully qualified URI to use. # @param headers - Request headers to set. # @param body - Request content body to set. # @param timeoutSec - Request timeout. # Returns a task which resolves to the contents of the response. func send_async(p_method : String, p_uri : String, p_headers : Dictionary, p_body : PackedByteArray): var req = HTTPRequest.new() req.timeout = timeout if use_threads and OS.get_name() != 'Web': req.use_threads = true # Threads not available nor needed on the web. # Parse method var method = HTTPClient.METHOD_GET if p_method == "POST": method = HTTPClient.METHOD_POST elif p_method == "PUT": method = HTTPClient.METHOD_PUT elif p_method == "DELETE": method = HTTPClient.METHOD_DELETE elif p_method == "HEAD": method = HTTPClient.METHOD_HEAD var headers = PackedStringArray() # Parse headers headers.append("Accept: application/json") for k in p_headers: headers.append("%s: %s" % [k, p_headers[k]]) id += 1 var retry = auto_retry_count if auto_retry else 0 var backoff = auto_retry_backoff_base _pending[id] = AsyncRequest.new(id, req, p_uri, method, headers, p_body, retry, backoff, logger) logger.debug("Sending request [ID: %d, Method: %s, Uri: %s, Headers: %s, Body: %s, Timeout: %d, Retries: %d, Backoff base: %d ms]" % [ id, p_method, p_uri, p_headers, p_body.get_string_from_utf8(), timeout, retry, backoff ]) add_child(req) return await _send_async(id, _pending) func get_last_token(): return id func cancel_request(p_token): if _pending.has(p_token): _pending[p_token].cancel() static func _clear_request(p_request : AsyncRequest, p_pending : Dictionary, p_id : int): if not p_request.request.is_queued_for_deletion(): p_request.logger.debug("Freeing request %d" % p_id) p_request.request.queue_free() p_pending.erase(p_id) static func _send_async(p_id : int, p_pending : Dictionary): var req : AsyncRequest = p_pending[p_id] await req.make_request() while req.result != HTTPRequest.RESULT_SUCCESS: req.logger.debug("Request %d failed with result: %d, response code: %d" % [ p_id, req.result, req.response_code ]) if not req.should_retry(): break await req.retry() _clear_request(req, p_pending, p_id) return req.parse_result() ================================================ FILE: addons/com.heroiclabs.nakama/Satori/SatoriSession.gd ================================================ extends SatoriAsyncResult class_name SatoriSession var _token: String = "" var token: String: get: return _token var _refresh_token: String = "" var refresh_token: String: get: return _refresh_token var _expire_time: int = 0 var expire_time: int: get: return _expire_time var expired: bool: get: return is_expired() var _refresh_expire_time: int = 0 var refresh_expire_time: int: get: return _refresh_expire_time var _identity_id: String = "" var identity_id: String: get: return _identity_id var _valid : bool = false var valid : bool: get: return _valid func is_expired() -> bool: return _expire_time < Time.get_unix_time_from_system() func would_expire_in(p_secs : int) -> bool: return _expire_time < Time.get_unix_time_from_system() + p_secs func has_refresh_expired(offset: float) -> bool: return _expire_time < offset func is_refresh_expired() -> bool: return _refresh_expire_time < Time.get_unix_time_from_system() func is_valid(): return _valid # Initializes a new instance of the SatoriSession class. # # @param p_token - The authentication token. # @param p_refresh_token - The refresh token. # @param p_exception - The exception to be thrown, if any. func _init(p_token = null, p_refresh_token = null, p_exception = null): super(p_exception) _refresh_expire_time = 0 if p_token: _update(p_token, p_refresh_token) func _update(p_token, p_refresh_token): _token = p_token _refresh_token = p_refresh_token var decoded = _jwt_unpack(p_token) if decoded.is_empty(): _valid = false return _valid = true _expire_time = int(decoded.get("exp", 0)) _identity_id = str(decoded.get("iid", "")) _refresh_expire_time = int(_jwt_unpack(refresh_token).get("exp", 0)) if !refresh_token.is_empty() else 0 func _to_string(): if is_exception(): return get_exception()._to_string() return "Session" % [ _token, _expire_time, _refresh_token, _refresh_expire_time, identity_id] func _jwt_unpack(p_token : String) -> Dictionary: # Hack decode JSON payload from JWT. if p_token.find(".") == -1: _ex = SatoriException.new("Missing payload: %s" % p_token) return {} var payload = p_token.split('.')[1]; var pad_length = ceil(payload.length() / 4.0) * 4; # Pad base64 for i in range(0, pad_length - payload.length()): payload += "=" payload = payload.replace("-", "+").replace("_", "/") var unpacked = Marshalls.base64_to_utf8(payload) var json = JSON.new() var error = json.parse(unpacked) if error == OK: var decoded = json.get_data() if typeof(decoded) == TYPE_DICTIONARY: return decoded _ex = SatoriException.new("Unable to unpack token: %s" % p_token) return {} ================================================ FILE: addons/com.heroiclabs.nakama/Satori/utils/SatoriAsyncResult.gd ================================================ extends RefCounted class_name SatoriAsyncResult var exception : SatoriException: set(v): pass get: return get_exception() var _ex = null func _init(p_ex = null): _ex = p_ex func is_exception(): return get_exception() != null func was_cancelled(): return is_exception() and get_exception().cancelled func get_exception() -> SatoriException: return _ex as SatoriException func _to_string(): if is_exception(): return get_exception()._to_string() return "SatoriAsyncResult<>" static func _safe_ret(p_obj, p_type : GDScript): if is_instance_of(p_obj, p_type): return p_obj elif p_obj is SatoriException: return p_type.new(p_obj) return p_type.new(SatoriException.new()) ================================================ FILE: addons/com.heroiclabs.nakama/Satori/utils/SatoriException.gd ================================================ extends RefCounted # An exception generated during a request. # Usually contains at least an error message. class_name SatoriException var _status_code : int = -1 var status_code : int: set(v): pass get: return _status_code var _grpc_status_code : int = -1 var grpc_status_code : int: set(v): pass get: return _grpc_status_code var _message : String = "" var message : String: set(v): pass get: return _message var _cancelled : bool = false var cancelled : bool: set(v): pass get: return _cancelled func _init(p_message : String = "", p_status_code : int = -1, p_grpc_status_code : int = -1, p_cancelled : bool = false): _status_code = p_status_code _grpc_status_code = p_grpc_status_code _message = p_message _cancelled = p_cancelled func _to_string() -> String: return "SatoriException(StatusCode={%s}, Message='{%s}', GrpcStatusCode={%s})" % [_status_code, _message, _grpc_status_code] ================================================ FILE: addons/com.heroiclabs.nakama/Satori/utils/SatoriLogger.gd ================================================ extends RefCounted class_name SatoriLogger enum LOG_LEVEL {NONE, ERROR, WARNING, INFO, VERBOSE, DEBUG} var _level = LOG_LEVEL.ERROR var _module = "Satori" func _init(p_module : String = "Satori", p_level : int = LOG_LEVEL.ERROR): _level = p_level _module = p_module func _log(level : int, msg): if level <= _level: if level == LOG_LEVEL.ERROR: printerr("=== %s : ERROR === %s" % [_module, str(msg)]) else: var what = "=== UNKNOWN === " for k in LOG_LEVEL: if level == LOG_LEVEL[k]: what = "=== %s : %s === " % [_module, k] break print(what + str(msg)) func error(msg): _log(LOG_LEVEL.ERROR, msg) func warning(msg): _log(LOG_LEVEL.WARNING, msg) func info(msg): _log(LOG_LEVEL.INFO, msg) func verbose(msg): _log(LOG_LEVEL.VERBOSE, msg) func debug(msg): _log(LOG_LEVEL.DEBUG, msg) ================================================ FILE: addons/com.heroiclabs.nakama/Satori/utils/SatoriSerializer.gd ================================================ extends RefCounted class_name SatoriSerializer static func serialize(p_obj : Object) -> Dictionary: var out = {} var schema = p_obj.get("_SCHEMA") if schema == null: return {} # No schema defined for k in schema: var prop = schema[k] var val = p_obj.get(prop["name"]) if val == null: continue var type = prop["type"] var content = prop.get("content", TYPE_NIL) if typeof(content) == TYPE_STRING: content = TYPE_OBJECT var val_type = typeof(val) match val_type: TYPE_OBJECT: # Simple objects out[k] = serialize(val) TYPE_ARRAY: # Array of objects var arr = [] if val.size() > 0 and typeof(val[0]) == TYPE_OBJECT: # Array of objects for e in val: arr.append(serialize(e)) else: arr = val out[k] = arr TYPE_PACKED_INT32_ARRAY, TYPE_PACKED_STRING_ARRAY: # Array of ints, bools, or strings var arr = [] for e in val: if content == TYPE_BOOL: e = bool(e) if typeof(e) != content: continue arr.append(e) out[k] = arr TYPE_DICTIONARY: # Maps var dict = {} if content == TYPE_OBJECT: # Map of objects for l in val: if typeof(val[l]) != TYPE_OBJECT: continue dict[l] = serialize(val[l]) else: # Map of simple types for l in val: var e = val[l] if content == TYPE_FLOAT: e = float(e) elif content == TYPE_INT: e = int(e) elif content == TYPE_BOOL: e = bool(e) if typeof(e) != content: continue dict[l] = e out[k] = dict _: out[k] = val return out static func deserialize(p_ns : GDScript, p_cls_name : String, p_dict : Dictionary) -> Object: var cls : GDScript = p_ns.get(p_cls_name) var schema = cls.get("_SCHEMA") if schema == null: return SatoriException.new() # No schema defined var obj = cls.new() for k in schema: var prop = schema[k] var pname = prop["name"] var type = prop["type"] var required = prop["required"] var content = prop.get("content", TYPE_NIL) var type_cmp = type if typeof(type) == TYPE_STRING: # A class type_cmp = TYPE_DICTIONARY if type_cmp == TYPE_PACKED_STRING_ARRAY or type_cmp == TYPE_PACKED_INT32_ARRAY: # A specialized array type_cmp = TYPE_ARRAY var content_cmp = content if typeof(content) == TYPE_STRING: # A dictionary or array of classes content_cmp = TYPE_DICTIONARY var val = p_dict.get(k, null) # Ints might and up being recognized as floats. Change that if needed if type_cmp == TYPE_INT: if typeof(val) == TYPE_FLOAT: val = int(val) elif typeof(val) == TYPE_STRING and val.is_valid_int(): val = val.to_int() if typeof(val) == type_cmp: if typeof(type) == TYPE_STRING: obj.set(pname, deserialize(p_ns, type, val)) elif type_cmp == TYPE_DICTIONARY: var v = {} for l in val: if typeof(content) == TYPE_STRING: v[l] = deserialize(p_ns, content, val[l]) elif content == TYPE_FLOAT: v[l] = float(val[l]) elif content == TYPE_INT: v[l] = int(val[l]) elif content == TYPE_BOOL: v[l] = bool(val[l]) else: v[l] = str(val[l]) obj.set(pname, v) elif type_cmp == TYPE_ARRAY: var v match content: TYPE_INT, TYPE_BOOL: v = PackedInt32Array() TYPE_STRING: v = PackedStringArray() _: v = Array() for e in val: if typeof(e) == TYPE_DICTIONARY: v.append(e) # Avoid deserialization if e is already a dictionary elif typeof(content) == TYPE_STRING: v.append(deserialize(p_ns, content, e)) elif content == TYPE_FLOAT: v.append(float(e)) elif content == TYPE_INT: v.append(int(e)) elif content == TYPE_BOOL: v.append(bool(e)) else: v.append(str(e)) obj.set(pname, v) else: obj.set(pname, val) elif required: obj._ex = SatoriException.new("ERROR [%s]: Missing or invalid required prop %s = %s:\n\t%s" % [p_cls_name, prop, p_dict.get(k), p_dict]) return obj return obj ### # Compatibility with Godot 3.1 which does not expose String.http_escape ### const HEX = ["0","1","2","3","4","5","6","7","8","9","A","B","C","D","E","F"] static func escape_http(p_str : String) -> String: var out : String = "" for o in p_str: if (o == '.' or o == '-' or o == '_' or o == '~' or (o >= 'a' and o <= 'z') or (o >= 'A' and o <= 'Z') or (o >= '0' and o <= '9')): out += o else: for b in o.to_utf8_buffer(): out += "%%%s" % to_hex(b) return out static func to_hex(p_val : int) -> String: var v := p_val var o := "" while v != 0: o = HEX[v % 16] + o v /= 16 return o ================================================ FILE: addons/com.heroiclabs.nakama/Satori.gd ================================================ @tool extends Node # The default host address of the server. const DEFAULT_HOST : String = "127.0.0.1" # The default port number of the server. const DEFAULT_PORT : int = 7450 # The default timeout for the connections. const DEFAULT_TIMEOUT = 15 # The default protocol scheme for the client connection. const DEFAULT_CLIENT_SCHEME : String = "http" # The default log level for the Satori logger. const DEFAULT_LOG_LEVEL = SatoriLogger.LOG_LEVEL.DEBUG var _http_adapter = null var logger = SatoriLogger.new() func _ready() -> void: process_mode = Node.PROCESS_MODE_ALWAYS func get_client_adapter() -> SatoriHTTPAdapter: if _http_adapter == null: _http_adapter = SatoriHTTPAdapter.new() _http_adapter.logger = logger _http_adapter.name = "SatoriHTTPAdapter" add_child(_http_adapter) return _http_adapter func create_client(p_api_key : String, p_host : String = DEFAULT_HOST, p_port : int = DEFAULT_PORT, p_scheme : String = DEFAULT_CLIENT_SCHEME, p_timeout : int = DEFAULT_TIMEOUT, p_log_level : int = DEFAULT_LOG_LEVEL, ) -> SatoriClient: logger._level = p_log_level return SatoriClient.new(get_client_adapter(), p_api_key, p_scheme, p_host, p_port, p_timeout) ================================================ FILE: addons/com.heroiclabs.nakama/api/NakamaAPI.gd ================================================ ### Code generated by codegen/main.go. DO NOT EDIT. ### extends RefCounted class_name NakamaAPI # A single user-role pair. class GroupUserListGroupUser extends NakamaAsyncResult: const _SCHEMA = { "state": {"name": "_state", "type": TYPE_INT, "required": false}, "user": {"name": "_user", "type": "ApiUser", "required": false}, } # Their relationship to the group. var _state var state : int: get: return 0 if not _state is int else int(_state) # User. var _user var user : ApiUser: get: return _user as ApiUser func _init(p_exception = null): super(p_exception) static func create(p_ns : GDScript, p_dict : Dictionary) -> GroupUserListGroupUser: return _safe_ret(NakamaSerializer.deserialize(p_ns, "GroupUserListGroupUser", p_dict), GroupUserListGroupUser) as GroupUserListGroupUser func serialize() -> Dictionary: return NakamaSerializer.serialize(self) func _to_string() -> String: if is_exception(): return get_exception()._to_string() var output : String = "" output += "state: %s, " % _state output += "user: %s, " % _user return output # A single group-role pair. class UserGroupListUserGroup extends NakamaAsyncResult: const _SCHEMA = { "group": {"name": "_group", "type": "ApiGroup", "required": false}, "state": {"name": "_state", "type": TYPE_INT, "required": false}, } # Group. var _group var group : ApiGroup: get: return _group as ApiGroup # The user's relationship to the group. var _state var state : int: get: return 0 if not _state is int else int(_state) func _init(p_exception = null): super(p_exception) static func create(p_ns : GDScript, p_dict : Dictionary) -> UserGroupListUserGroup: return _safe_ret(NakamaSerializer.deserialize(p_ns, "UserGroupListUserGroup", p_dict), UserGroupListUserGroup) as UserGroupListUserGroup func serialize() -> Dictionary: return NakamaSerializer.serialize(self) func _to_string() -> String: if is_exception(): return get_exception()._to_string() var output : String = "" output += "group: %s, " % _group output += "state: %s, " % _state return output # Record values to write. class WriteLeaderboardRecordRequestLeaderboardRecordWrite extends NakamaAsyncResult: const _SCHEMA = { "metadata": {"name": "_metadata", "type": TYPE_STRING, "required": false}, "operator": {"name": "_operator", "type": TYPE_INT, "required": false}, "score": {"name": "_score", "type": TYPE_STRING, "required": false}, "subscore": {"name": "_subscore", "type": TYPE_STRING, "required": false}, } # Optional record metadata. var _metadata var metadata : String: get: return "" if not _metadata is String else String(_metadata) # Operator override. var _operator var operator : int: get: return ApiOperator.values()[0] if not ApiOperator.values().has(_operator) else _operator # The score value to submit. var _score var score : String: get: return "" if not _score is String else String(_score) # An optional secondary value. var _subscore var subscore : String: get: return "" if not _subscore is String else String(_subscore) func _init(p_exception = null): super(p_exception) static func create(p_ns : GDScript, p_dict : Dictionary) -> WriteLeaderboardRecordRequestLeaderboardRecordWrite: return _safe_ret(NakamaSerializer.deserialize(p_ns, "WriteLeaderboardRecordRequestLeaderboardRecordWrite", p_dict), WriteLeaderboardRecordRequestLeaderboardRecordWrite) as WriteLeaderboardRecordRequestLeaderboardRecordWrite func serialize() -> Dictionary: return NakamaSerializer.serialize(self) func _to_string() -> String: if is_exception(): return get_exception()._to_string() var output : String = "" output += "metadata: %s, " % _metadata output += "operator: %s, " % _operator output += "score: %s, " % _score output += "subscore: %s, " % _subscore return output # Record values to write. class WriteTournamentRecordRequestTournamentRecordWrite extends NakamaAsyncResult: const _SCHEMA = { "metadata": {"name": "_metadata", "type": TYPE_STRING, "required": false}, "operator": {"name": "_operator", "type": TYPE_INT, "required": false}, "score": {"name": "_score", "type": TYPE_STRING, "required": false}, "subscore": {"name": "_subscore", "type": TYPE_STRING, "required": false}, } # A JSON object of additional properties (optional). var _metadata var metadata : String: get: return "" if not _metadata is String else String(_metadata) # Operator override. var _operator var operator : int: get: return ApiOperator.values()[0] if not ApiOperator.values().has(_operator) else _operator # The score value to submit. var _score var score : String: get: return "" if not _score is String else String(_score) # An optional secondary value. var _subscore var subscore : String: get: return "" if not _subscore is String else String(_subscore) func _init(p_exception = null): super(p_exception) static func create(p_ns : GDScript, p_dict : Dictionary) -> WriteTournamentRecordRequestTournamentRecordWrite: return _safe_ret(NakamaSerializer.deserialize(p_ns, "WriteTournamentRecordRequestTournamentRecordWrite", p_dict), WriteTournamentRecordRequestTournamentRecordWrite) as WriteTournamentRecordRequestTournamentRecordWrite func serialize() -> Dictionary: return NakamaSerializer.serialize(self) func _to_string() -> String: if is_exception(): return get_exception()._to_string() var output : String = "" output += "metadata: %s, " % _metadata output += "operator: %s, " % _operator output += "score: %s, " % _score output += "subscore: %s, " % _subscore return output # A user with additional account details. Always the current user. class ApiAccount extends NakamaAsyncResult: const _SCHEMA = { "custom_id": {"name": "_custom_id", "type": TYPE_STRING, "required": false}, "devices": {"name": "_devices", "type": TYPE_ARRAY, "required": false, "content": "ApiAccountDevice"}, "disable_time": {"name": "_disable_time", "type": TYPE_STRING, "required": false}, "email": {"name": "_email", "type": TYPE_STRING, "required": false}, "user": {"name": "_user", "type": "ApiUser", "required": false}, "verify_time": {"name": "_verify_time", "type": TYPE_STRING, "required": false}, "wallet": {"name": "_wallet", "type": TYPE_STRING, "required": false}, } # The custom id in the user's account. var _custom_id var custom_id : String: get: return "" if not _custom_id is String else String(_custom_id) # The devices which belong to the user's account. var _devices var devices : Array: get: return Array() if not _devices is Array else Array(_devices) # The UNIX time when the user's account was disabled/banned. var _disable_time var disable_time : String: get: return "" if not _disable_time is String else String(_disable_time) # The email address of the user. var _email var email : String: get: return "" if not _email is String else String(_email) # The user object. var _user var user : ApiUser: get: return _user as ApiUser # The UNIX time when the user's email was verified. var _verify_time var verify_time : String: get: return "" if not _verify_time is String else String(_verify_time) # The user's wallet data. var _wallet var wallet : String: get: return "" if not _wallet is String else String(_wallet) var _wallet_dict = null var wallet_dict : Dictionary: get: if _wallet_dict == null: if _wallet == null: return {} var json = JSON.new() if json.parse(_wallet) != OK: return {} _wallet_dict = json.get_data() return _wallet_dict as Dictionary func _init(p_exception = null): super(p_exception) static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiAccount: return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiAccount", p_dict), ApiAccount) as ApiAccount func serialize() -> Dictionary: return NakamaSerializer.serialize(self) func _to_string() -> String: if is_exception(): return get_exception()._to_string() var output : String = "" output += "custom_id: %s, " % _custom_id output += "devices: %s, " % [_devices] output += "disable_time: %s, " % _disable_time output += "email: %s, " % _email output += "user: %s, " % _user output += "verify_time: %s, " % _verify_time output += "wallet: %s, " % _wallet return output # Send a Apple Sign In token to the server. Used with authenticate/link/unlink. class ApiAccountApple extends NakamaAsyncResult: const _SCHEMA = { "token": {"name": "_token", "type": TYPE_STRING, "required": false}, "vars": {"name": "_vars", "type": TYPE_DICTIONARY, "required": false, "content": TYPE_STRING}, } # The ID token received from Apple to validate. var _token var token : String: get: return "" if not _token is String else String(_token) # Extra information that will be bundled in the session token. var _vars var vars : Dictionary: get: return Dictionary() if not _vars is Dictionary else _vars.duplicate() func _init(p_exception = null): super(p_exception) static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiAccountApple: return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiAccountApple", p_dict), ApiAccountApple) as ApiAccountApple func serialize() -> Dictionary: return NakamaSerializer.serialize(self) func _to_string() -> String: if is_exception(): return get_exception()._to_string() var output : String = "" output += "token: %s, " % _token var map_string : String = "" if typeof(_vars) == TYPE_DICTIONARY: for k in _vars: map_string += "{%s=%s}, " % [k, _vars[k]] output += "vars: [%s], " % map_string return output # Send a custom ID to the server. Used with authenticate/link/unlink. class ApiAccountCustom extends NakamaAsyncResult: const _SCHEMA = { "id": {"name": "_id", "type": TYPE_STRING, "required": false}, "vars": {"name": "_vars", "type": TYPE_DICTIONARY, "required": false, "content": TYPE_STRING}, } # A custom identifier. var _id var id : String: get: return "" if not _id is String else String(_id) # Extra information that will be bundled in the session token. var _vars var vars : Dictionary: get: return Dictionary() if not _vars is Dictionary else _vars.duplicate() func _init(p_exception = null): super(p_exception) static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiAccountCustom: return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiAccountCustom", p_dict), ApiAccountCustom) as ApiAccountCustom func serialize() -> Dictionary: return NakamaSerializer.serialize(self) func _to_string() -> String: if is_exception(): return get_exception()._to_string() var output : String = "" output += "id: %s, " % _id var map_string : String = "" if typeof(_vars) == TYPE_DICTIONARY: for k in _vars: map_string += "{%s=%s}, " % [k, _vars[k]] output += "vars: [%s], " % map_string return output # Send a device to the server. Used with authenticate/link/unlink and user. class ApiAccountDevice extends NakamaAsyncResult: const _SCHEMA = { "id": {"name": "_id", "type": TYPE_STRING, "required": false}, "vars": {"name": "_vars", "type": TYPE_DICTIONARY, "required": false, "content": TYPE_STRING}, } # A device identifier. Should be obtained by a platform-specific device API. var _id var id : String: get: return "" if not _id is String else String(_id) # Extra information that will be bundled in the session token. var _vars var vars : Dictionary: get: return Dictionary() if not _vars is Dictionary else _vars.duplicate() func _init(p_exception = null): super(p_exception) static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiAccountDevice: return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiAccountDevice", p_dict), ApiAccountDevice) as ApiAccountDevice func serialize() -> Dictionary: return NakamaSerializer.serialize(self) func _to_string() -> String: if is_exception(): return get_exception()._to_string() var output : String = "" output += "id: %s, " % _id var map_string : String = "" if typeof(_vars) == TYPE_DICTIONARY: for k in _vars: map_string += "{%s=%s}, " % [k, _vars[k]] output += "vars: [%s], " % map_string return output # Send an email with password to the server. Used with authenticate/link/unlink. class ApiAccountEmail extends NakamaAsyncResult: const _SCHEMA = { "email": {"name": "_email", "type": TYPE_STRING, "required": false}, "password": {"name": "_password", "type": TYPE_STRING, "required": false}, "vars": {"name": "_vars", "type": TYPE_DICTIONARY, "required": false, "content": TYPE_STRING}, } # A valid RFC-5322 email address. var _email var email : String: get: return "" if not _email is String else String(_email) # A password for the user account. var _password var password : String: get: return "" if not _password is String else String(_password) # Extra information that will be bundled in the session token. var _vars var vars : Dictionary: get: return Dictionary() if not _vars is Dictionary else _vars.duplicate() func _init(p_exception = null): super(p_exception) static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiAccountEmail: return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiAccountEmail", p_dict), ApiAccountEmail) as ApiAccountEmail func serialize() -> Dictionary: return NakamaSerializer.serialize(self) func _to_string() -> String: if is_exception(): return get_exception()._to_string() var output : String = "" output += "email: %s, " % _email output += "password: %s, " % _password var map_string : String = "" if typeof(_vars) == TYPE_DICTIONARY: for k in _vars: map_string += "{%s=%s}, " % [k, _vars[k]] output += "vars: [%s], " % map_string return output # Send a Facebook token to the server. Used with authenticate/link/unlink. class ApiAccountFacebook extends NakamaAsyncResult: const _SCHEMA = { "token": {"name": "_token", "type": TYPE_STRING, "required": false}, "vars": {"name": "_vars", "type": TYPE_DICTIONARY, "required": false, "content": TYPE_STRING}, } # The OAuth token received from Facebook to access their profile API. var _token var token : String: get: return "" if not _token is String else String(_token) # Extra information that will be bundled in the session token. var _vars var vars : Dictionary: get: return Dictionary() if not _vars is Dictionary else _vars.duplicate() func _init(p_exception = null): super(p_exception) static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiAccountFacebook: return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiAccountFacebook", p_dict), ApiAccountFacebook) as ApiAccountFacebook func serialize() -> Dictionary: return NakamaSerializer.serialize(self) func _to_string() -> String: if is_exception(): return get_exception()._to_string() var output : String = "" output += "token: %s, " % _token var map_string : String = "" if typeof(_vars) == TYPE_DICTIONARY: for k in _vars: map_string += "{%s=%s}, " % [k, _vars[k]] output += "vars: [%s], " % map_string return output # Send a Facebook Instant Game token to the server. Used with authenticate/link/unlink. class ApiAccountFacebookInstantGame extends NakamaAsyncResult: const _SCHEMA = { "signed_player_info": {"name": "_signed_player_info", "type": TYPE_STRING, "required": false}, "vars": {"name": "_vars", "type": TYPE_DICTIONARY, "required": false, "content": TYPE_STRING}, } # var _signed_player_info var signed_player_info : String: get: return "" if not _signed_player_info is String else String(_signed_player_info) # Extra information that will be bundled in the session token. var _vars var vars : Dictionary: get: return Dictionary() if not _vars is Dictionary else _vars.duplicate() func _init(p_exception = null): super(p_exception) static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiAccountFacebookInstantGame: return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiAccountFacebookInstantGame", p_dict), ApiAccountFacebookInstantGame) as ApiAccountFacebookInstantGame func serialize() -> Dictionary: return NakamaSerializer.serialize(self) func _to_string() -> String: if is_exception(): return get_exception()._to_string() var output : String = "" output += "signed_player_info: %s, " % _signed_player_info var map_string : String = "" if typeof(_vars) == TYPE_DICTIONARY: for k in _vars: map_string += "{%s=%s}, " % [k, _vars[k]] output += "vars: [%s], " % map_string return output # Send Apple's Game Center account credentials to the server. Used with authenticate/link/unlink. class ApiAccountGameCenter extends NakamaAsyncResult: const _SCHEMA = { "bundle_id": {"name": "_bundle_id", "type": TYPE_STRING, "required": false}, "player_id": {"name": "_player_id", "type": TYPE_STRING, "required": false}, "public_key_url": {"name": "_public_key_url", "type": TYPE_STRING, "required": false}, "salt": {"name": "_salt", "type": TYPE_STRING, "required": false}, "signature": {"name": "_signature", "type": TYPE_STRING, "required": false}, "timestamp_seconds": {"name": "_timestamp_seconds", "type": TYPE_STRING, "required": false}, "vars": {"name": "_vars", "type": TYPE_DICTIONARY, "required": false, "content": TYPE_STRING}, } # Bundle ID (generated by GameCenter). var _bundle_id var bundle_id : String: get: return "" if not _bundle_id is String else String(_bundle_id) # Player ID (generated by GameCenter). var _player_id var player_id : String: get: return "" if not _player_id is String else String(_player_id) # The URL for the public encryption key. var _public_key_url var public_key_url : String: get: return "" if not _public_key_url is String else String(_public_key_url) # A random "NSString" used to compute the hash and keep it randomized. var _salt var salt : String: get: return "" if not _salt is String else String(_salt) # The verification signature data generated. var _signature var signature : String: get: return "" if not _signature is String else String(_signature) # Time since UNIX epoch when the signature was created. var _timestamp_seconds var timestamp_seconds : String: get: return "" if not _timestamp_seconds is String else String(_timestamp_seconds) # Extra information that will be bundled in the session token. var _vars var vars : Dictionary: get: return Dictionary() if not _vars is Dictionary else _vars.duplicate() func _init(p_exception = null): super(p_exception) static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiAccountGameCenter: return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiAccountGameCenter", p_dict), ApiAccountGameCenter) as ApiAccountGameCenter func serialize() -> Dictionary: return NakamaSerializer.serialize(self) func _to_string() -> String: if is_exception(): return get_exception()._to_string() var output : String = "" output += "bundle_id: %s, " % _bundle_id output += "player_id: %s, " % _player_id output += "public_key_url: %s, " % _public_key_url output += "salt: %s, " % _salt output += "signature: %s, " % _signature output += "timestamp_seconds: %s, " % _timestamp_seconds var map_string : String = "" if typeof(_vars) == TYPE_DICTIONARY: for k in _vars: map_string += "{%s=%s}, " % [k, _vars[k]] output += "vars: [%s], " % map_string return output # Send a Google token to the server. Used with authenticate/link/unlink. class ApiAccountGoogle extends NakamaAsyncResult: const _SCHEMA = { "token": {"name": "_token", "type": TYPE_STRING, "required": false}, "vars": {"name": "_vars", "type": TYPE_DICTIONARY, "required": false, "content": TYPE_STRING}, } # The OAuth token received from Google to access their profile API. var _token var token : String: get: return "" if not _token is String else String(_token) # Extra information that will be bundled in the session token. var _vars var vars : Dictionary: get: return Dictionary() if not _vars is Dictionary else _vars.duplicate() func _init(p_exception = null): super(p_exception) static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiAccountGoogle: return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiAccountGoogle", p_dict), ApiAccountGoogle) as ApiAccountGoogle func serialize() -> Dictionary: return NakamaSerializer.serialize(self) func _to_string() -> String: if is_exception(): return get_exception()._to_string() var output : String = "" output += "token: %s, " % _token var map_string : String = "" if typeof(_vars) == TYPE_DICTIONARY: for k in _vars: map_string += "{%s=%s}, " % [k, _vars[k]] output += "vars: [%s], " % map_string return output # Send a Steam token to the server. Used with authenticate/link/unlink. class ApiAccountSteam extends NakamaAsyncResult: const _SCHEMA = { "token": {"name": "_token", "type": TYPE_STRING, "required": false}, "vars": {"name": "_vars", "type": TYPE_DICTIONARY, "required": false, "content": TYPE_STRING}, } # The account token received from Steam to access their profile API. var _token var token : String: get: return "" if not _token is String else String(_token) # Extra information that will be bundled in the session token. var _vars var vars : Dictionary: get: return Dictionary() if not _vars is Dictionary else _vars.duplicate() func _init(p_exception = null): super(p_exception) static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiAccountSteam: return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiAccountSteam", p_dict), ApiAccountSteam) as ApiAccountSteam func serialize() -> Dictionary: return NakamaSerializer.serialize(self) func _to_string() -> String: if is_exception(): return get_exception()._to_string() var output : String = "" output += "token: %s, " % _token var map_string : String = "" if typeof(_vars) == TYPE_DICTIONARY: for k in _vars: map_string += "{%s=%s}, " % [k, _vars[k]] output += "vars: [%s], " % map_string return output # A message sent on a channel. class ApiChannelMessage extends NakamaAsyncResult: const _SCHEMA = { "channel_id": {"name": "_channel_id", "type": TYPE_STRING, "required": false}, "code": {"name": "_code", "type": TYPE_INT, "required": false}, "content": {"name": "_content", "type": TYPE_STRING, "required": false}, "create_time": {"name": "_create_time", "type": TYPE_STRING, "required": false}, "group_id": {"name": "_group_id", "type": TYPE_STRING, "required": false}, "message_id": {"name": "_message_id", "type": TYPE_STRING, "required": false}, "persistent": {"name": "_persistent", "type": TYPE_BOOL, "required": false}, "room_name": {"name": "_room_name", "type": TYPE_STRING, "required": false}, "sender_id": {"name": "_sender_id", "type": TYPE_STRING, "required": false}, "update_time": {"name": "_update_time", "type": TYPE_STRING, "required": false}, "user_id_one": {"name": "_user_id_one", "type": TYPE_STRING, "required": false}, "user_id_two": {"name": "_user_id_two", "type": TYPE_STRING, "required": false}, "username": {"name": "_username", "type": TYPE_STRING, "required": false}, } # The channel this message belongs to. var _channel_id var channel_id : String: get: return "" if not _channel_id is String else String(_channel_id) # The code representing a message type or category. var _code var code : int: get: return 0 if not _code is int else int(_code) # The content payload. var _content var content : String: get: return "" if not _content is String else String(_content) # The UNIX time when the message was created. var _create_time var create_time : String: get: return "" if not _create_time is String else String(_create_time) # The ID of the group, or an empty string if this message was not sent through a group channel. var _group_id var group_id : String: get: return "" if not _group_id is String else String(_group_id) # The unique ID of this message. var _message_id var message_id : String: get: return "" if not _message_id is String else String(_message_id) # True if the message was persisted to the channel's history, false otherwise. var _persistent var persistent : bool: get: return false if not _persistent is bool else bool(_persistent) # The name of the chat room, or an empty string if this message was not sent through a chat room. var _room_name var room_name : String: get: return "" if not _room_name is String else String(_room_name) # Message sender, usually a user ID. var _sender_id var sender_id : String: get: return "" if not _sender_id is String else String(_sender_id) # The UNIX time when the message was last updated. var _update_time var update_time : String: get: return "" if not _update_time is String else String(_update_time) # The ID of the first DM user, or an empty string if this message was not sent through a DM chat. var _user_id_one var user_id_one : String: get: return "" if not _user_id_one is String else String(_user_id_one) # The ID of the second DM user, or an empty string if this message was not sent through a DM chat. var _user_id_two var user_id_two : String: get: return "" if not _user_id_two is String else String(_user_id_two) # The username of the message sender, if any. var _username var username : String: get: return "" if not _username is String else String(_username) func _init(p_exception = null): super(p_exception) static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiChannelMessage: return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiChannelMessage", p_dict), ApiChannelMessage) as ApiChannelMessage func serialize() -> Dictionary: return NakamaSerializer.serialize(self) func _to_string() -> String: if is_exception(): return get_exception()._to_string() var output : String = "" output += "channel_id: %s, " % _channel_id output += "code: %s, " % _code output += "content: %s, " % _content output += "create_time: %s, " % _create_time output += "group_id: %s, " % _group_id output += "message_id: %s, " % _message_id output += "persistent: %s, " % _persistent output += "room_name: %s, " % _room_name output += "sender_id: %s, " % _sender_id output += "update_time: %s, " % _update_time output += "user_id_one: %s, " % _user_id_one output += "user_id_two: %s, " % _user_id_two output += "username: %s, " % _username return output # A list of channel messages, usually a result of a list operation. class ApiChannelMessageList extends NakamaAsyncResult: const _SCHEMA = { "cacheable_cursor": {"name": "_cacheable_cursor", "type": TYPE_STRING, "required": false}, "messages": {"name": "_messages", "type": TYPE_ARRAY, "required": false, "content": "ApiChannelMessage"}, "next_cursor": {"name": "_next_cursor", "type": TYPE_STRING, "required": false}, "prev_cursor": {"name": "_prev_cursor", "type": TYPE_STRING, "required": false}, } # Cacheable cursor to list newer messages. Durable and designed to be stored, unlike next/prev cursors. var _cacheable_cursor var cacheable_cursor : String: get: return "" if not _cacheable_cursor is String else String(_cacheable_cursor) # A list of messages. var _messages var messages : Array: get: return Array() if not _messages is Array else Array(_messages) # The cursor to send when retrieving the next page, if any. var _next_cursor var next_cursor : String: get: return "" if not _next_cursor is String else String(_next_cursor) # The cursor to send when retrieving the previous page, if any. var _prev_cursor var prev_cursor : String: get: return "" if not _prev_cursor is String else String(_prev_cursor) func _init(p_exception = null): super(p_exception) static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiChannelMessageList: return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiChannelMessageList", p_dict), ApiChannelMessageList) as ApiChannelMessageList func serialize() -> Dictionary: return NakamaSerializer.serialize(self) func _to_string() -> String: if is_exception(): return get_exception()._to_string() var output : String = "" output += "cacheable_cursor: %s, " % _cacheable_cursor output += "messages: %s, " % [_messages] output += "next_cursor: %s, " % _next_cursor output += "prev_cursor: %s, " % _prev_cursor return output # Create a group with the current user as owner. class ApiCreateGroupRequest extends NakamaAsyncResult: const _SCHEMA = { "avatar_url": {"name": "_avatar_url", "type": TYPE_STRING, "required": false}, "description": {"name": "_description", "type": TYPE_STRING, "required": false}, "lang_tag": {"name": "_lang_tag", "type": TYPE_STRING, "required": false}, "max_count": {"name": "_max_count", "type": TYPE_INT, "required": false}, "name": {"name": "_name", "type": TYPE_STRING, "required": false}, "open": {"name": "_open", "type": TYPE_BOOL, "required": false}, } # A URL for an avatar image. var _avatar_url var avatar_url : String: get: return "" if not _avatar_url is String else String(_avatar_url) # A description for the group. var _description var description : String: get: return "" if not _description is String else String(_description) # The language expected to be a tag which follows the BCP-47 spec. var _lang_tag var lang_tag : String: get: return "" if not _lang_tag is String else String(_lang_tag) # Maximum number of group members. var _max_count var max_count : int: get: return 0 if not _max_count is int else int(_max_count) # A unique name for the group. var _name var name : String: get: return "" if not _name is String else String(_name) # Mark a group as open or not where only admins can accept members. var _open var open : bool: get: return false if not _open is bool else bool(_open) func _init(p_exception = null): super(p_exception) static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiCreateGroupRequest: return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiCreateGroupRequest", p_dict), ApiCreateGroupRequest) as ApiCreateGroupRequest func serialize() -> Dictionary: return NakamaSerializer.serialize(self) func _to_string() -> String: if is_exception(): return get_exception()._to_string() var output : String = "" output += "avatar_url: %s, " % _avatar_url output += "description: %s, " % _description output += "lang_tag: %s, " % _lang_tag output += "max_count: %s, " % _max_count output += "name: %s, " % _name output += "open: %s, " % _open return output # Storage objects to delete. class ApiDeleteStorageObjectId extends NakamaAsyncResult: const _SCHEMA = { "collection": {"name": "_collection", "type": TYPE_STRING, "required": false}, "key": {"name": "_key", "type": TYPE_STRING, "required": false}, "version": {"name": "_version", "type": TYPE_STRING, "required": false}, } # The collection which stores the object. var _collection var collection : String: get: return "" if not _collection is String else String(_collection) # The key of the object within the collection. var _key var key : String: get: return "" if not _key is String else String(_key) # The version hash of the object. var _version var version : String: get: return "" if not _version is String else String(_version) func _init(p_exception = null): super(p_exception) static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiDeleteStorageObjectId: return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiDeleteStorageObjectId", p_dict), ApiDeleteStorageObjectId) as ApiDeleteStorageObjectId func serialize() -> Dictionary: return NakamaSerializer.serialize(self) func _to_string() -> String: if is_exception(): return get_exception()._to_string() var output : String = "" output += "collection: %s, " % _collection output += "key: %s, " % _key output += "version: %s, " % _version return output # Batch delete storage objects. class ApiDeleteStorageObjectsRequest extends NakamaAsyncResult: const _SCHEMA = { "object_ids": {"name": "_object_ids", "type": TYPE_ARRAY, "required": false, "content": "ApiDeleteStorageObjectId"}, } # Batch of storage objects. var _object_ids var object_ids : Array: get: return Array() if not _object_ids is Array else Array(_object_ids) func _init(p_exception = null): super(p_exception) static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiDeleteStorageObjectsRequest: return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiDeleteStorageObjectsRequest", p_dict), ApiDeleteStorageObjectsRequest) as ApiDeleteStorageObjectsRequest func serialize() -> Dictionary: return NakamaSerializer.serialize(self) func _to_string() -> String: if is_exception(): return get_exception()._to_string() var output : String = "" output += "object_ids: %s, " % [_object_ids] return output # Represents an event to be passed through the server to registered event handlers. class ApiEvent extends NakamaAsyncResult: const _SCHEMA = { "external": {"name": "_external", "type": TYPE_BOOL, "required": false}, "name": {"name": "_name", "type": TYPE_STRING, "required": false}, "properties": {"name": "_properties", "type": TYPE_DICTIONARY, "required": false, "content": TYPE_STRING}, "timestamp": {"name": "_timestamp", "type": TYPE_STRING, "required": false}, } # True if the event came directly from a client call, false otherwise. var _external var external : bool: get: return false if not _external is bool else bool(_external) # An event name, type, category, or identifier. var _name var name : String: get: return "" if not _name is String else String(_name) # Arbitrary event property values. var _properties var properties : Dictionary: get: return Dictionary() if not _properties is Dictionary else _properties.duplicate() # The time when the event was triggered. var _timestamp var timestamp : String: get: return "" if not _timestamp is String else String(_timestamp) func _init(p_exception = null): super(p_exception) static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiEvent: return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiEvent", p_dict), ApiEvent) as ApiEvent func serialize() -> Dictionary: return NakamaSerializer.serialize(self) func _to_string() -> String: if is_exception(): return get_exception()._to_string() var output : String = "" output += "external: %s, " % _external output += "name: %s, " % _name var map_string : String = "" if typeof(_properties) == TYPE_DICTIONARY: for k in _properties: map_string += "{%s=%s}, " % [k, _properties[k]] output += "properties: [%s], " % map_string output += "timestamp: %s, " % _timestamp return output # A friend of a user. class ApiFriend extends NakamaAsyncResult: const _SCHEMA = { "state": {"name": "_state", "type": TYPE_INT, "required": false}, "update_time": {"name": "_update_time", "type": TYPE_STRING, "required": false}, "user": {"name": "_user", "type": "ApiUser", "required": false}, } # The friend status. var _state var state : int: get: return 0 if not _state is int else int(_state) # Time of the latest relationship update. var _update_time var update_time : String: get: return "" if not _update_time is String else String(_update_time) # The user object. var _user var user : ApiUser: get: return _user as ApiUser func _init(p_exception = null): super(p_exception) static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiFriend: return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiFriend", p_dict), ApiFriend) as ApiFriend func serialize() -> Dictionary: return NakamaSerializer.serialize(self) func _to_string() -> String: if is_exception(): return get_exception()._to_string() var output : String = "" output += "state: %s, " % _state output += "update_time: %s, " % _update_time output += "user: %s, " % _user return output # A collection of zero or more friends of the user. class ApiFriendList extends NakamaAsyncResult: const _SCHEMA = { "cursor": {"name": "_cursor", "type": TYPE_STRING, "required": false}, "friends": {"name": "_friends", "type": TYPE_ARRAY, "required": false, "content": "ApiFriend"}, } # Cursor for the next page of results, if any. var _cursor var cursor : String: get: return "" if not _cursor is String else String(_cursor) # The Friend objects. var _friends var friends : Array: get: return Array() if not _friends is Array else Array(_friends) func _init(p_exception = null): super(p_exception) static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiFriendList: return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiFriendList", p_dict), ApiFriendList) as ApiFriendList func serialize() -> Dictionary: return NakamaSerializer.serialize(self) func _to_string() -> String: if is_exception(): return get_exception()._to_string() var output : String = "" output += "cursor: %s, " % _cursor output += "friends: %s, " % [_friends] return output # A group in the server. class ApiGroup extends NakamaAsyncResult: const _SCHEMA = { "avatar_url": {"name": "_avatar_url", "type": TYPE_STRING, "required": false}, "create_time": {"name": "_create_time", "type": TYPE_STRING, "required": false}, "creator_id": {"name": "_creator_id", "type": TYPE_STRING, "required": false}, "description": {"name": "_description", "type": TYPE_STRING, "required": false}, "edge_count": {"name": "_edge_count", "type": TYPE_INT, "required": false}, "id": {"name": "_id", "type": TYPE_STRING, "required": false}, "lang_tag": {"name": "_lang_tag", "type": TYPE_STRING, "required": false}, "max_count": {"name": "_max_count", "type": TYPE_INT, "required": false}, "metadata": {"name": "_metadata", "type": TYPE_STRING, "required": false}, "name": {"name": "_name", "type": TYPE_STRING, "required": false}, "open": {"name": "_open", "type": TYPE_BOOL, "required": false}, "update_time": {"name": "_update_time", "type": TYPE_STRING, "required": false}, } # A URL for an avatar image. var _avatar_url var avatar_url : String: get: return "" if not _avatar_url is String else String(_avatar_url) # The UNIX time when the group was created. var _create_time var create_time : String: get: return "" if not _create_time is String else String(_create_time) # The id of the user who created the group. var _creator_id var creator_id : String: get: return "" if not _creator_id is String else String(_creator_id) # A description for the group. var _description var description : String: get: return "" if not _description is String else String(_description) # The current count of all members in the group. var _edge_count var edge_count : int: get: return 0 if not _edge_count is int else int(_edge_count) # The id of a group. var _id var id : String: get: return "" if not _id is String else String(_id) # The language expected to be a tag which follows the BCP-47 spec. var _lang_tag var lang_tag : String: get: return "" if not _lang_tag is String else String(_lang_tag) # The maximum number of members allowed. var _max_count var max_count : int: get: return 0 if not _max_count is int else int(_max_count) # Additional information stored as a JSON object. var _metadata var metadata : String: get: return "" if not _metadata is String else String(_metadata) # The unique name of the group. var _name var name : String: get: return "" if not _name is String else String(_name) # Anyone can join open groups, otherwise only admins can accept members. var _open var open : bool: get: return false if not _open is bool else bool(_open) # The UNIX time when the group was last updated. var _update_time var update_time : String: get: return "" if not _update_time is String else String(_update_time) func _init(p_exception = null): super(p_exception) static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiGroup: return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiGroup", p_dict), ApiGroup) as ApiGroup func serialize() -> Dictionary: return NakamaSerializer.serialize(self) func _to_string() -> String: if is_exception(): return get_exception()._to_string() var output : String = "" output += "avatar_url: %s, " % _avatar_url output += "create_time: %s, " % _create_time output += "creator_id: %s, " % _creator_id output += "description: %s, " % _description output += "edge_count: %s, " % _edge_count output += "id: %s, " % _id output += "lang_tag: %s, " % _lang_tag output += "max_count: %s, " % _max_count output += "metadata: %s, " % _metadata output += "name: %s, " % _name output += "open: %s, " % _open output += "update_time: %s, " % _update_time return output # One or more groups returned from a listing operation. class ApiGroupList extends NakamaAsyncResult: const _SCHEMA = { "cursor": {"name": "_cursor", "type": TYPE_STRING, "required": false}, "groups": {"name": "_groups", "type": TYPE_ARRAY, "required": false, "content": "ApiGroup"}, } # A cursor used to get the next page. var _cursor var cursor : String: get: return "" if not _cursor is String else String(_cursor) # One or more groups. var _groups var groups : Array: get: return Array() if not _groups is Array else Array(_groups) func _init(p_exception = null): super(p_exception) static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiGroupList: return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiGroupList", p_dict), ApiGroupList) as ApiGroupList func serialize() -> Dictionary: return NakamaSerializer.serialize(self) func _to_string() -> String: if is_exception(): return get_exception()._to_string() var output : String = "" output += "cursor: %s, " % _cursor output += "groups: %s, " % [_groups] return output # A list of users belonging to a group, along with their role. class ApiGroupUserList extends NakamaAsyncResult: const _SCHEMA = { "cursor": {"name": "_cursor", "type": TYPE_STRING, "required": false}, "group_users": {"name": "_group_users", "type": TYPE_ARRAY, "required": false, "content": "GroupUserListGroupUser"}, } # Cursor for the next page of results, if any. var _cursor var cursor : String: get: return "" if not _cursor is String else String(_cursor) # User-role pairs for a group. var _group_users var group_users : Array: get: return Array() if not _group_users is Array else Array(_group_users) func _init(p_exception = null): super(p_exception) static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiGroupUserList: return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiGroupUserList", p_dict), ApiGroupUserList) as ApiGroupUserList func serialize() -> Dictionary: return NakamaSerializer.serialize(self) func _to_string() -> String: if is_exception(): return get_exception()._to_string() var output : String = "" output += "cursor: %s, " % _cursor output += "group_users: %s, " % [_group_users] return output # Represents a complete leaderboard record with all scores and associated metadata. class ApiLeaderboardRecord extends NakamaAsyncResult: const _SCHEMA = { "create_time": {"name": "_create_time", "type": TYPE_STRING, "required": false}, "expiry_time": {"name": "_expiry_time", "type": TYPE_STRING, "required": false}, "leaderboard_id": {"name": "_leaderboard_id", "type": TYPE_STRING, "required": false}, "max_num_score": {"name": "_max_num_score", "type": TYPE_INT, "required": false}, "metadata": {"name": "_metadata", "type": TYPE_STRING, "required": false}, "num_score": {"name": "_num_score", "type": TYPE_INT, "required": false}, "owner_id": {"name": "_owner_id", "type": TYPE_STRING, "required": false}, "rank": {"name": "_rank", "type": TYPE_STRING, "required": false}, "score": {"name": "_score", "type": TYPE_STRING, "required": false}, "subscore": {"name": "_subscore", "type": TYPE_STRING, "required": false}, "update_time": {"name": "_update_time", "type": TYPE_STRING, "required": false}, "username": {"name": "_username", "type": TYPE_STRING, "required": false}, } # The UNIX time when the leaderboard record was created. var _create_time var create_time : String: get: return "" if not _create_time is String else String(_create_time) # The UNIX time when the leaderboard record expires. var _expiry_time var expiry_time : String: get: return "" if not _expiry_time is String else String(_expiry_time) # The ID of the leaderboard this score belongs to. var _leaderboard_id var leaderboard_id : String: get: return "" if not _leaderboard_id is String else String(_leaderboard_id) # The maximum number of score updates allowed by the owner. var _max_num_score var max_num_score : int: get: return 0 if not _max_num_score is int else int(_max_num_score) # Metadata. var _metadata var metadata : String: get: return "" if not _metadata is String else String(_metadata) # The number of submissions to this score record. var _num_score var num_score : int: get: return 0 if not _num_score is int else int(_num_score) # The ID of the score owner, usually a user or group. var _owner_id var owner_id : String: get: return "" if not _owner_id is String else String(_owner_id) # The rank of this record. var _rank var rank : String: get: return "" if not _rank is String else String(_rank) # The score value. var _score var score : String: get: return "" if not _score is String else String(_score) # An optional subscore value. var _subscore var subscore : String: get: return "" if not _subscore is String else String(_subscore) # The UNIX time when the leaderboard record was updated. var _update_time var update_time : String: get: return "" if not _update_time is String else String(_update_time) # The username of the score owner, if the owner is a user. var _username var username : String: get: return "" if not _username is String else String(_username) func _init(p_exception = null): super(p_exception) static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiLeaderboardRecord: return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiLeaderboardRecord", p_dict), ApiLeaderboardRecord) as ApiLeaderboardRecord func serialize() -> Dictionary: return NakamaSerializer.serialize(self) func _to_string() -> String: if is_exception(): return get_exception()._to_string() var output : String = "" output += "create_time: %s, " % _create_time output += "expiry_time: %s, " % _expiry_time output += "leaderboard_id: %s, " % _leaderboard_id output += "max_num_score: %s, " % _max_num_score output += "metadata: %s, " % _metadata output += "num_score: %s, " % _num_score output += "owner_id: %s, " % _owner_id output += "rank: %s, " % _rank output += "score: %s, " % _score output += "subscore: %s, " % _subscore output += "update_time: %s, " % _update_time output += "username: %s, " % _username return output # A set of leaderboard records, may be part of a leaderboard records page or a batch of individual records. class ApiLeaderboardRecordList extends NakamaAsyncResult: const _SCHEMA = { "next_cursor": {"name": "_next_cursor", "type": TYPE_STRING, "required": false}, "owner_records": {"name": "_owner_records", "type": TYPE_ARRAY, "required": false, "content": "ApiLeaderboardRecord"}, "prev_cursor": {"name": "_prev_cursor", "type": TYPE_STRING, "required": false}, "records": {"name": "_records", "type": TYPE_ARRAY, "required": false, "content": "ApiLeaderboardRecord"}, } # The cursor to send when retrieving the next page, if any. var _next_cursor var next_cursor : String: get: return "" if not _next_cursor is String else String(_next_cursor) # A batched set of leaderboard records belonging to specified owners. var _owner_records var owner_records : Array: get: return Array() if not _owner_records is Array else Array(_owner_records) # The cursor to send when retrieving the previous page, if any. var _prev_cursor var prev_cursor : String: get: return "" if not _prev_cursor is String else String(_prev_cursor) # A list of leaderboard records. var _records var records : Array: get: return Array() if not _records is Array else Array(_records) func _init(p_exception = null): super(p_exception) static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiLeaderboardRecordList: return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiLeaderboardRecordList", p_dict), ApiLeaderboardRecordList) as ApiLeaderboardRecordList func serialize() -> Dictionary: return NakamaSerializer.serialize(self) func _to_string() -> String: if is_exception(): return get_exception()._to_string() var output : String = "" output += "next_cursor: %s, " % _next_cursor output += "owner_records: %s, " % [_owner_records] output += "prev_cursor: %s, " % _prev_cursor output += "records: %s, " % [_records] return output # Link Steam to the current user's account. class ApiLinkSteamRequest extends NakamaAsyncResult: const _SCHEMA = { "account": {"name": "_account", "type": "ApiAccountSteam", "required": false}, "sync": {"name": "_sync", "type": TYPE_BOOL, "required": false}, } # The Facebook account details. var _account var account : ApiAccountSteam: get: return _account as ApiAccountSteam # Import Steam friends for the user. var _sync var sync : bool: get: return false if not _sync is bool else bool(_sync) func _init(p_exception = null): super(p_exception) static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiLinkSteamRequest: return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiLinkSteamRequest", p_dict), ApiLinkSteamRequest) as ApiLinkSteamRequest func serialize() -> Dictionary: return NakamaSerializer.serialize(self) func _to_string() -> String: if is_exception(): return get_exception()._to_string() var output : String = "" output += "account: %s, " % _account output += "sync: %s, " % _sync return output # List user subscriptions. class ApiListSubscriptionsRequest extends NakamaAsyncResult: const _SCHEMA = { "cursor": {"name": "_cursor", "type": TYPE_STRING, "required": false}, "limit": {"name": "_limit", "type": TYPE_INT, "required": false}, } # var _cursor var cursor : String: get: return "" if not _cursor is String else String(_cursor) # var _limit var limit : int: get: return 0 if not _limit is int else int(_limit) func _init(p_exception = null): super(p_exception) static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiListSubscriptionsRequest: return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiListSubscriptionsRequest", p_dict), ApiListSubscriptionsRequest) as ApiListSubscriptionsRequest func serialize() -> Dictionary: return NakamaSerializer.serialize(self) func _to_string() -> String: if is_exception(): return get_exception()._to_string() var output : String = "" output += "cursor: %s, " % _cursor output += "limit: %s, " % _limit return output # Represents a realtime match. class ApiMatch extends NakamaAsyncResult: const _SCHEMA = { "authoritative": {"name": "_authoritative", "type": TYPE_BOOL, "required": false}, "handler_name": {"name": "_handler_name", "type": TYPE_STRING, "required": false}, "label": {"name": "_label", "type": TYPE_STRING, "required": false}, "match_id": {"name": "_match_id", "type": TYPE_STRING, "required": false}, "size": {"name": "_size", "type": TYPE_INT, "required": false}, "tick_rate": {"name": "_tick_rate", "type": TYPE_INT, "required": false}, } # True if it's an server-managed authoritative match, false otherwise. var _authoritative var authoritative : bool: get: return false if not _authoritative is bool else bool(_authoritative) # var _handler_name var handler_name : String: get: return "" if not _handler_name is String else String(_handler_name) # Match label, if any. var _label var label : String: get: return "" if not _label is String else String(_label) # The ID of the match, can be used to join. var _match_id var match_id : String: get: return "" if not _match_id is String else String(_match_id) # Current number of users in the match. var _size var size : int: get: return 0 if not _size is int else int(_size) # var _tick_rate var tick_rate : int: get: return 0 if not _tick_rate is int else int(_tick_rate) func _init(p_exception = null): super(p_exception) static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiMatch: return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiMatch", p_dict), ApiMatch) as ApiMatch func serialize() -> Dictionary: return NakamaSerializer.serialize(self) func _to_string() -> String: if is_exception(): return get_exception()._to_string() var output : String = "" output += "authoritative: %s, " % _authoritative output += "handler_name: %s, " % _handler_name output += "label: %s, " % _label output += "match_id: %s, " % _match_id output += "size: %s, " % _size output += "tick_rate: %s, " % _tick_rate return output # A list of realtime matches. class ApiMatchList extends NakamaAsyncResult: const _SCHEMA = { "matches": {"name": "_matches", "type": TYPE_ARRAY, "required": false, "content": "ApiMatch"}, } # A number of matches corresponding to a list operation. var _matches var matches : Array: get: return Array() if not _matches is Array else Array(_matches) func _init(p_exception = null): super(p_exception) static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiMatchList: return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiMatchList", p_dict), ApiMatchList) as ApiMatchList func serialize() -> Dictionary: return NakamaSerializer.serialize(self) func _to_string() -> String: if is_exception(): return get_exception()._to_string() var output : String = "" output += "matches: %s, " % [_matches] return output # A notification in the server. class ApiNotification extends NakamaAsyncResult: const _SCHEMA = { "code": {"name": "_code", "type": TYPE_INT, "required": false}, "content": {"name": "_content", "type": TYPE_STRING, "required": false}, "create_time": {"name": "_create_time", "type": TYPE_STRING, "required": false}, "id": {"name": "_id", "type": TYPE_STRING, "required": false}, "persistent": {"name": "_persistent", "type": TYPE_BOOL, "required": false}, "sender_id": {"name": "_sender_id", "type": TYPE_STRING, "required": false}, "subject": {"name": "_subject", "type": TYPE_STRING, "required": false}, } # Category code for this notification. var _code var code : int: get: return 0 if not _code is int else int(_code) # Content of the notification in JSON. var _content var content : String: get: return "" if not _content is String else String(_content) # The UNIX time when the notification was created. var _create_time var create_time : String: get: return "" if not _create_time is String else String(_create_time) # ID of the Notification. var _id var id : String: get: return "" if not _id is String else String(_id) # True if this notification was persisted to the database. var _persistent var persistent : bool: get: return false if not _persistent is bool else bool(_persistent) # ID of the sender, if a user. Otherwise 'null'. var _sender_id var sender_id : String: get: return "" if not _sender_id is String else String(_sender_id) # Subject of the notification. var _subject var subject : String: get: return "" if not _subject is String else String(_subject) func _init(p_exception = null): super(p_exception) static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiNotification: return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiNotification", p_dict), ApiNotification) as ApiNotification func serialize() -> Dictionary: return NakamaSerializer.serialize(self) func _to_string() -> String: if is_exception(): return get_exception()._to_string() var output : String = "" output += "code: %s, " % _code output += "content: %s, " % _content output += "create_time: %s, " % _create_time output += "id: %s, " % _id output += "persistent: %s, " % _persistent output += "sender_id: %s, " % _sender_id output += "subject: %s, " % _subject return output # A collection of zero or more notifications. class ApiNotificationList extends NakamaAsyncResult: const _SCHEMA = { "cacheable_cursor": {"name": "_cacheable_cursor", "type": TYPE_STRING, "required": false}, "notifications": {"name": "_notifications", "type": TYPE_ARRAY, "required": false, "content": "ApiNotification"}, } # Use this cursor to paginate notifications. Cache this to catch up to new notifications. var _cacheable_cursor var cacheable_cursor : String: get: return "" if not _cacheable_cursor is String else String(_cacheable_cursor) # Collection of notifications. var _notifications var notifications : Array: get: return Array() if not _notifications is Array else Array(_notifications) func _init(p_exception = null): super(p_exception) static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiNotificationList: return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiNotificationList", p_dict), ApiNotificationList) as ApiNotificationList func serialize() -> Dictionary: return NakamaSerializer.serialize(self) func _to_string() -> String: if is_exception(): return get_exception()._to_string() var output : String = "" output += "cacheable_cursor: %s, " % _cacheable_cursor output += "notifications: %s, " % [_notifications] return output # Operator that can be used to override the one set in the leaderboard. # - NO_OVERRIDE: Do not override the leaderboard operator. # - BEST: Override the leaderboard operator with BEST. # - SET: Override the leaderboard operator with SET. # - INCREMENT: Override the leaderboard operator with INCREMENT. # - DECREMENT: Override the leaderboard operator with DECREMENT.# [ - NO_OVERRIDE: Do not override the leaderboard operator. - BEST: Override the leaderboard operator with BEST. - SET: Override the leaderboard operator with SET. - INCREMENT: Override the leaderboard operator with INCREMENT. - DECREMENT: Override the leaderboard operator with DECREMENT.] enum ApiOperator {NO_OVERRIDE = 0,BEST = 1,SET = 2,INCREMENT = 3,DECREMENT = 4,} # Storage objects to get. class ApiReadStorageObjectId extends NakamaAsyncResult: const _SCHEMA = { "collection": {"name": "_collection", "type": TYPE_STRING, "required": false}, "key": {"name": "_key", "type": TYPE_STRING, "required": false}, "user_id": {"name": "_user_id", "type": TYPE_STRING, "required": false}, } # The collection which stores the object. var _collection var collection : String: get: return "" if not _collection is String else String(_collection) # The key of the object within the collection. var _key var key : String: get: return "" if not _key is String else String(_key) # The user owner of the object. var _user_id var user_id : String: get: return "" if not _user_id is String else String(_user_id) func _init(p_exception = null): super(p_exception) static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiReadStorageObjectId: return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiReadStorageObjectId", p_dict), ApiReadStorageObjectId) as ApiReadStorageObjectId func serialize() -> Dictionary: return NakamaSerializer.serialize(self) func _to_string() -> String: if is_exception(): return get_exception()._to_string() var output : String = "" output += "collection: %s, " % _collection output += "key: %s, " % _key output += "user_id: %s, " % _user_id return output # Batch get storage objects. class ApiReadStorageObjectsRequest extends NakamaAsyncResult: const _SCHEMA = { "object_ids": {"name": "_object_ids", "type": TYPE_ARRAY, "required": false, "content": "ApiReadStorageObjectId"}, } # Batch of storage objects. var _object_ids var object_ids : Array: get: return Array() if not _object_ids is Array else Array(_object_ids) func _init(p_exception = null): super(p_exception) static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiReadStorageObjectsRequest: return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiReadStorageObjectsRequest", p_dict), ApiReadStorageObjectsRequest) as ApiReadStorageObjectsRequest func serialize() -> Dictionary: return NakamaSerializer.serialize(self) func _to_string() -> String: if is_exception(): return get_exception()._to_string() var output : String = "" output += "object_ids: %s, " % [_object_ids] return output # Execute an Lua function on the server. class ApiRpc extends NakamaAsyncResult: const _SCHEMA = { "http_key": {"name": "_http_key", "type": TYPE_STRING, "required": false}, "id": {"name": "_id", "type": TYPE_STRING, "required": false}, "payload": {"name": "_payload", "type": TYPE_STRING, "required": false}, } # The authentication key used when executed as a non-client HTTP request. var _http_key var http_key : String: get: return "" if not _http_key is String else String(_http_key) # The identifier of the function. var _id var id : String: get: return "" if not _id is String else String(_id) # The payload of the function which must be a JSON object. var _payload var payload : String: get: return "" if not _payload is String else String(_payload) func _init(p_exception = null): super(p_exception) static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiRpc: return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiRpc", p_dict), ApiRpc) as ApiRpc func serialize() -> Dictionary: return NakamaSerializer.serialize(self) func _to_string() -> String: if is_exception(): return get_exception()._to_string() var output : String = "" output += "http_key: %s, " % _http_key output += "id: %s, " % _id output += "payload: %s, " % _payload return output # A user's session used to authenticate messages. class ApiSession extends NakamaAsyncResult: const _SCHEMA = { "created": {"name": "_created", "type": TYPE_BOOL, "required": false}, "refresh_token": {"name": "_refresh_token", "type": TYPE_STRING, "required": false}, "token": {"name": "_token", "type": TYPE_STRING, "required": false}, } # True if the corresponding account was just created, false otherwise. var _created var created : bool: get: return false if not _created is bool else bool(_created) # Refresh token that can be used for session token renewal. var _refresh_token var refresh_token : String: get: return "" if not _refresh_token is String else String(_refresh_token) # Authentication credentials. var _token var token : String: get: return "" if not _token is String else String(_token) func _init(p_exception = null): super(p_exception) static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiSession: return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiSession", p_dict), ApiSession) as ApiSession func serialize() -> Dictionary: return NakamaSerializer.serialize(self) func _to_string() -> String: if is_exception(): return get_exception()._to_string() var output : String = "" output += "created: %s, " % _created output += "refresh_token: %s, " % _refresh_token output += "token: %s, " % _token return output # Log out a session, invalidate a refresh token, or log out all sessions/refresh tokens for a user. class ApiSessionLogoutRequest extends NakamaAsyncResult: const _SCHEMA = { "refresh_token": {"name": "_refresh_token", "type": TYPE_STRING, "required": false}, "token": {"name": "_token", "type": TYPE_STRING, "required": false}, } # Refresh token to invalidate. var _refresh_token var refresh_token : String: get: return "" if not _refresh_token is String else String(_refresh_token) # Session token to log out. var _token var token : String: get: return "" if not _token is String else String(_token) func _init(p_exception = null): super(p_exception) static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiSessionLogoutRequest: return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiSessionLogoutRequest", p_dict), ApiSessionLogoutRequest) as ApiSessionLogoutRequest func serialize() -> Dictionary: return NakamaSerializer.serialize(self) func _to_string() -> String: if is_exception(): return get_exception()._to_string() var output : String = "" output += "refresh_token: %s, " % _refresh_token output += "token: %s, " % _token return output # Authenticate against the server with a refresh token. class ApiSessionRefreshRequest extends NakamaAsyncResult: const _SCHEMA = { "token": {"name": "_token", "type": TYPE_STRING, "required": false}, "vars": {"name": "_vars", "type": TYPE_DICTIONARY, "required": false, "content": TYPE_STRING}, } # Refresh token. var _token var token : String: get: return "" if not _token is String else String(_token) # Extra information that will be bundled in the session token. var _vars var vars : Dictionary: get: return Dictionary() if not _vars is Dictionary else _vars.duplicate() func _init(p_exception = null): super(p_exception) static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiSessionRefreshRequest: return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiSessionRefreshRequest", p_dict), ApiSessionRefreshRequest) as ApiSessionRefreshRequest func serialize() -> Dictionary: return NakamaSerializer.serialize(self) func _to_string() -> String: if is_exception(): return get_exception()._to_string() var output : String = "" output += "token: %s, " % _token var map_string : String = "" if typeof(_vars) == TYPE_DICTIONARY: for k in _vars: map_string += "{%s=%s}, " % [k, _vars[k]] output += "vars: [%s], " % map_string return output # An object within the storage engine. class ApiStorageObject extends NakamaAsyncResult: const _SCHEMA = { "collection": {"name": "_collection", "type": TYPE_STRING, "required": false}, "create_time": {"name": "_create_time", "type": TYPE_STRING, "required": false}, "key": {"name": "_key", "type": TYPE_STRING, "required": false}, "permission_read": {"name": "_permission_read", "type": TYPE_INT, "required": false}, "permission_write": {"name": "_permission_write", "type": TYPE_INT, "required": false}, "update_time": {"name": "_update_time", "type": TYPE_STRING, "required": false}, "user_id": {"name": "_user_id", "type": TYPE_STRING, "required": false}, "value": {"name": "_value", "type": TYPE_STRING, "required": false}, "version": {"name": "_version", "type": TYPE_STRING, "required": false}, } # The collection which stores the object. var _collection var collection : String: get: return "" if not _collection is String else String(_collection) # The UNIX time when the object was created. var _create_time var create_time : String: get: return "" if not _create_time is String else String(_create_time) # The key of the object within the collection. var _key var key : String: get: return "" if not _key is String else String(_key) # The read access permissions for the object. var _permission_read var permission_read : int: get: return 0 if not _permission_read is int else int(_permission_read) # The write access permissions for the object. var _permission_write var permission_write : int: get: return 0 if not _permission_write is int else int(_permission_write) # The UNIX time when the object was last updated. var _update_time var update_time : String: get: return "" if not _update_time is String else String(_update_time) # The user owner of the object. var _user_id var user_id : String: get: return "" if not _user_id is String else String(_user_id) # The value of the object. var _value var value : String: get: return "" if not _value is String else String(_value) # The version hash of the object. var _version var version : String: get: return "" if not _version is String else String(_version) func _init(p_exception = null): super(p_exception) static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiStorageObject: return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiStorageObject", p_dict), ApiStorageObject) as ApiStorageObject func serialize() -> Dictionary: return NakamaSerializer.serialize(self) func _to_string() -> String: if is_exception(): return get_exception()._to_string() var output : String = "" output += "collection: %s, " % _collection output += "create_time: %s, " % _create_time output += "key: %s, " % _key output += "permission_read: %s, " % _permission_read output += "permission_write: %s, " % _permission_write output += "update_time: %s, " % _update_time output += "user_id: %s, " % _user_id output += "value: %s, " % _value output += "version: %s, " % _version return output # A storage acknowledgement. class ApiStorageObjectAck extends NakamaAsyncResult: const _SCHEMA = { "collection": {"name": "_collection", "type": TYPE_STRING, "required": false}, "key": {"name": "_key", "type": TYPE_STRING, "required": false}, "user_id": {"name": "_user_id", "type": TYPE_STRING, "required": false}, "version": {"name": "_version", "type": TYPE_STRING, "required": false}, } # The collection which stores the object. var _collection var collection : String: get: return "" if not _collection is String else String(_collection) # The key of the object within the collection. var _key var key : String: get: return "" if not _key is String else String(_key) # The owner of the object. var _user_id var user_id : String: get: return "" if not _user_id is String else String(_user_id) # The version hash of the object. var _version var version : String: get: return "" if not _version is String else String(_version) func _init(p_exception = null): super(p_exception) static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiStorageObjectAck: return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiStorageObjectAck", p_dict), ApiStorageObjectAck) as ApiStorageObjectAck func serialize() -> Dictionary: return NakamaSerializer.serialize(self) func _to_string() -> String: if is_exception(): return get_exception()._to_string() var output : String = "" output += "collection: %s, " % _collection output += "key: %s, " % _key output += "user_id: %s, " % _user_id output += "version: %s, " % _version return output # Batch of acknowledgements for the storage object write. class ApiStorageObjectAcks extends NakamaAsyncResult: const _SCHEMA = { "acks": {"name": "_acks", "type": TYPE_ARRAY, "required": false, "content": "ApiStorageObjectAck"}, } # Batch of storage write acknowledgements. var _acks var acks : Array: get: return Array() if not _acks is Array else Array(_acks) func _init(p_exception = null): super(p_exception) static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiStorageObjectAcks: return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiStorageObjectAcks", p_dict), ApiStorageObjectAcks) as ApiStorageObjectAcks func serialize() -> Dictionary: return NakamaSerializer.serialize(self) func _to_string() -> String: if is_exception(): return get_exception()._to_string() var output : String = "" output += "acks: %s, " % [_acks] return output # List of storage objects. class ApiStorageObjectList extends NakamaAsyncResult: const _SCHEMA = { "cursor": {"name": "_cursor", "type": TYPE_STRING, "required": false}, "objects": {"name": "_objects", "type": TYPE_ARRAY, "required": false, "content": "ApiStorageObject"}, } # The cursor for the next page of results, if any. var _cursor var cursor : String: get: return "" if not _cursor is String else String(_cursor) # The list of storage objects. var _objects var objects : Array: get: return Array() if not _objects is Array else Array(_objects) func _init(p_exception = null): super(p_exception) static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiStorageObjectList: return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiStorageObjectList", p_dict), ApiStorageObjectList) as ApiStorageObjectList func serialize() -> Dictionary: return NakamaSerializer.serialize(self) func _to_string() -> String: if is_exception(): return get_exception()._to_string() var output : String = "" output += "cursor: %s, " % _cursor output += "objects: %s, " % [_objects] return output # Batch of storage objects. class ApiStorageObjects extends NakamaAsyncResult: const _SCHEMA = { "objects": {"name": "_objects", "type": TYPE_ARRAY, "required": false, "content": "ApiStorageObject"}, } # The batch of storage objects. var _objects var objects : Array: get: return Array() if not _objects is Array else Array(_objects) func _init(p_exception = null): super(p_exception) static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiStorageObjects: return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiStorageObjects", p_dict), ApiStorageObjects) as ApiStorageObjects func serialize() -> Dictionary: return NakamaSerializer.serialize(self) func _to_string() -> String: if is_exception(): return get_exception()._to_string() var output : String = "" output += "objects: %s, " % [_objects] return output # Environment where a purchase/subscription took place, # - UNKNOWN: Unknown environment. # - SANDBOX: Sandbox/test environment. # - PRODUCTION: Production environment.# [- UNKNOWN: Unknown environment. - SANDBOX: Sandbox/test environment. - PRODUCTION: Production environment.] enum ApiStoreEnvironment {UNKNOWN = 0,SANDBOX = 1,PRODUCTION = 2,} # Validation Provider, # - APPLE_APP_STORE: Apple App Store # - GOOGLE_PLAY_STORE: Google Play Store # - HUAWEI_APP_GALLERY: Huawei App Gallery# [- APPLE_APP_STORE: Apple App Store - GOOGLE_PLAY_STORE: Google Play Store - HUAWEI_APP_GALLERY: Huawei App Gallery] enum ApiStoreProvider {APPLE_APP_STORE = 0,GOOGLE_PLAY_STORE = 1,HUAWEI_APP_GALLERY = 2,} # A list of validated subscriptions stored by Nakama. class ApiSubscriptionList extends NakamaAsyncResult: const _SCHEMA = { "cursor": {"name": "_cursor", "type": TYPE_STRING, "required": false}, "prev_cursor": {"name": "_prev_cursor", "type": TYPE_STRING, "required": false}, "validated_subscriptions": {"name": "_validated_subscriptions", "type": TYPE_ARRAY, "required": false, "content": "ApiValidatedSubscription"}, } # The cursor to send when retrieving the next page, if any. var _cursor var cursor : String: get: return "" if not _cursor is String else String(_cursor) # The cursor to send when retrieving the previous page, if any. var _prev_cursor var prev_cursor : String: get: return "" if not _prev_cursor is String else String(_prev_cursor) # Stored validated subscriptions. var _validated_subscriptions var validated_subscriptions : Array: get: return Array() if not _validated_subscriptions is Array else Array(_validated_subscriptions) func _init(p_exception = null): super(p_exception) static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiSubscriptionList: return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiSubscriptionList", p_dict), ApiSubscriptionList) as ApiSubscriptionList func serialize() -> Dictionary: return NakamaSerializer.serialize(self) func _to_string() -> String: if is_exception(): return get_exception()._to_string() var output : String = "" output += "cursor: %s, " % _cursor output += "prev_cursor: %s, " % _prev_cursor output += "validated_subscriptions: %s, " % [_validated_subscriptions] return output # A tournament on the server. class ApiTournament extends NakamaAsyncResult: const _SCHEMA = { "authoritative": {"name": "_authoritative", "type": TYPE_BOOL, "required": false}, "can_enter": {"name": "_can_enter", "type": TYPE_BOOL, "required": false}, "category": {"name": "_category", "type": TYPE_INT, "required": false}, "create_time": {"name": "_create_time", "type": TYPE_STRING, "required": false}, "description": {"name": "_description", "type": TYPE_STRING, "required": false}, "duration": {"name": "_duration", "type": TYPE_INT, "required": false}, "end_active": {"name": "_end_active", "type": TYPE_INT, "required": false}, "end_time": {"name": "_end_time", "type": TYPE_STRING, "required": false}, "id": {"name": "_id", "type": TYPE_STRING, "required": false}, "max_num_score": {"name": "_max_num_score", "type": TYPE_INT, "required": false}, "max_size": {"name": "_max_size", "type": TYPE_INT, "required": false}, "metadata": {"name": "_metadata", "type": TYPE_STRING, "required": false}, "next_reset": {"name": "_next_reset", "type": TYPE_INT, "required": false}, "operator": {"name": "_operator", "type": TYPE_INT, "required": false}, "prev_reset": {"name": "_prev_reset", "type": TYPE_INT, "required": false}, "size": {"name": "_size", "type": TYPE_INT, "required": false}, "sort_order": {"name": "_sort_order", "type": TYPE_INT, "required": false}, "start_active": {"name": "_start_active", "type": TYPE_INT, "required": false}, "start_time": {"name": "_start_time", "type": TYPE_STRING, "required": false}, "title": {"name": "_title", "type": TYPE_STRING, "required": false}, } # Whether the leaderboard was created authoritatively or not. var _authoritative var authoritative : bool: get: return false if not _authoritative is bool else bool(_authoritative) # True if the tournament is active and can enter. A computed value. var _can_enter var can_enter : bool: get: return false if not _can_enter is bool else bool(_can_enter) # The category of the tournament. e.g. "vip" could be category 1. var _category var category : int: get: return 0 if not _category is int else int(_category) # The UNIX time when the tournament was created. var _create_time var create_time : String: get: return "" if not _create_time is String else String(_create_time) # The description of the tournament. May be blank. var _description var description : String: get: return "" if not _description is String else String(_description) # Duration of the tournament in seconds. var _duration var duration : int: get: return 0 if not _duration is int else int(_duration) # The UNIX time when the tournament stops being active until next reset. A computed value. var _end_active var end_active : int: get: return 0 if not _end_active is int else int(_end_active) # The UNIX time when the tournament will be stopped. var _end_time var end_time : String: get: return "" if not _end_time is String else String(_end_time) # The ID of the tournament. var _id var id : String: get: return "" if not _id is String else String(_id) # The maximum score updates allowed per player for the current tournament. var _max_num_score var max_num_score : int: get: return 0 if not _max_num_score is int else int(_max_num_score) # The maximum number of players for the tournament. var _max_size var max_size : int: get: return 0 if not _max_size is int else int(_max_size) # Additional information stored as a JSON object. var _metadata var metadata : String: get: return "" if not _metadata is String else String(_metadata) # The UNIX time when the tournament is next playable. A computed value. var _next_reset var next_reset : int: get: return 0 if not _next_reset is int else int(_next_reset) # Operator. var _operator var operator : int: get: return ApiOperator.values()[0] if not ApiOperator.values().has(_operator) else _operator # The UNIX time when the tournament was last reset. A computed value. var _prev_reset var prev_reset : int: get: return 0 if not _prev_reset is int else int(_prev_reset) # The current number of players in the tournament. var _size var size : int: get: return 0 if not _size is int else int(_size) # ASC (0) or DESC (1) sort mode of scores in the tournament. var _sort_order var sort_order : int: get: return 0 if not _sort_order is int else int(_sort_order) # The UNIX time when the tournament start being active. A computed value. var _start_active var start_active : int: get: return 0 if not _start_active is int else int(_start_active) # The UNIX time when the tournament will start. var _start_time var start_time : String: get: return "" if not _start_time is String else String(_start_time) # The title for the tournament. var _title var title : String: get: return "" if not _title is String else String(_title) func _init(p_exception = null): super(p_exception) static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiTournament: return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiTournament", p_dict), ApiTournament) as ApiTournament func serialize() -> Dictionary: return NakamaSerializer.serialize(self) func _to_string() -> String: if is_exception(): return get_exception()._to_string() var output : String = "" output += "authoritative: %s, " % _authoritative output += "can_enter: %s, " % _can_enter output += "category: %s, " % _category output += "create_time: %s, " % _create_time output += "description: %s, " % _description output += "duration: %s, " % _duration output += "end_active: %s, " % _end_active output += "end_time: %s, " % _end_time output += "id: %s, " % _id output += "max_num_score: %s, " % _max_num_score output += "max_size: %s, " % _max_size output += "metadata: %s, " % _metadata output += "next_reset: %s, " % _next_reset output += "operator: %s, " % _operator output += "prev_reset: %s, " % _prev_reset output += "size: %s, " % _size output += "sort_order: %s, " % _sort_order output += "start_active: %s, " % _start_active output += "start_time: %s, " % _start_time output += "title: %s, " % _title return output # A list of tournaments. class ApiTournamentList extends NakamaAsyncResult: const _SCHEMA = { "cursor": {"name": "_cursor", "type": TYPE_STRING, "required": false}, "tournaments": {"name": "_tournaments", "type": TYPE_ARRAY, "required": false, "content": "ApiTournament"}, } # A pagination cursor (optional). var _cursor var cursor : String: get: return "" if not _cursor is String else String(_cursor) # The list of tournaments returned. var _tournaments var tournaments : Array: get: return Array() if not _tournaments is Array else Array(_tournaments) func _init(p_exception = null): super(p_exception) static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiTournamentList: return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiTournamentList", p_dict), ApiTournamentList) as ApiTournamentList func serialize() -> Dictionary: return NakamaSerializer.serialize(self) func _to_string() -> String: if is_exception(): return get_exception()._to_string() var output : String = "" output += "cursor: %s, " % _cursor output += "tournaments: %s, " % [_tournaments] return output # A set of tournament records which may be part of a tournament records page or a batch of individual records. class ApiTournamentRecordList extends NakamaAsyncResult: const _SCHEMA = { "next_cursor": {"name": "_next_cursor", "type": TYPE_STRING, "required": false}, "owner_records": {"name": "_owner_records", "type": TYPE_ARRAY, "required": false, "content": "ApiLeaderboardRecord"}, "prev_cursor": {"name": "_prev_cursor", "type": TYPE_STRING, "required": false}, "records": {"name": "_records", "type": TYPE_ARRAY, "required": false, "content": "ApiLeaderboardRecord"}, } # The cursor to send when retireving the next page (optional). var _next_cursor var next_cursor : String: get: return "" if not _next_cursor is String else String(_next_cursor) # A batched set of tournament records belonging to specified owners. var _owner_records var owner_records : Array: get: return Array() if not _owner_records is Array else Array(_owner_records) # The cursor to send when retrieving the previous page (optional). var _prev_cursor var prev_cursor : String: get: return "" if not _prev_cursor is String else String(_prev_cursor) # A list of tournament records. var _records var records : Array: get: return Array() if not _records is Array else Array(_records) func _init(p_exception = null): super(p_exception) static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiTournamentRecordList: return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiTournamentRecordList", p_dict), ApiTournamentRecordList) as ApiTournamentRecordList func serialize() -> Dictionary: return NakamaSerializer.serialize(self) func _to_string() -> String: if is_exception(): return get_exception()._to_string() var output : String = "" output += "next_cursor: %s, " % _next_cursor output += "owner_records: %s, " % [_owner_records] output += "prev_cursor: %s, " % _prev_cursor output += "records: %s, " % [_records] return output # Update a user's account details. class ApiUpdateAccountRequest extends NakamaAsyncResult: const _SCHEMA = { "avatar_url": {"name": "_avatar_url", "type": TYPE_STRING, "required": false}, "display_name": {"name": "_display_name", "type": TYPE_STRING, "required": false}, "lang_tag": {"name": "_lang_tag", "type": TYPE_STRING, "required": false}, "location": {"name": "_location", "type": TYPE_STRING, "required": false}, "timezone": {"name": "_timezone", "type": TYPE_STRING, "required": false}, "username": {"name": "_username", "type": TYPE_STRING, "required": false}, } # A URL for an avatar image. var _avatar_url var avatar_url : String: get: return "" if not _avatar_url is String else String(_avatar_url) # The display name of the user. var _display_name var display_name : String: get: return "" if not _display_name is String else String(_display_name) # The language expected to be a tag which follows the BCP-47 spec. var _lang_tag var lang_tag : String: get: return "" if not _lang_tag is String else String(_lang_tag) # The location set by the user. var _location var location : String: get: return "" if not _location is String else String(_location) # The timezone set by the user. var _timezone var timezone : String: get: return "" if not _timezone is String else String(_timezone) # The username of the user's account. var _username var username : String: get: return "" if not _username is String else String(_username) func _init(p_exception = null): super(p_exception) static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiUpdateAccountRequest: return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiUpdateAccountRequest", p_dict), ApiUpdateAccountRequest) as ApiUpdateAccountRequest func serialize() -> Dictionary: return NakamaSerializer.serialize(self) func _to_string() -> String: if is_exception(): return get_exception()._to_string() var output : String = "" output += "avatar_url: %s, " % _avatar_url output += "display_name: %s, " % _display_name output += "lang_tag: %s, " % _lang_tag output += "location: %s, " % _location output += "timezone: %s, " % _timezone output += "username: %s, " % _username return output # Update fields in a given group. class ApiUpdateGroupRequest extends NakamaAsyncResult: const _SCHEMA = { "avatar_url": {"name": "_avatar_url", "type": TYPE_STRING, "required": false}, "description": {"name": "_description", "type": TYPE_STRING, "required": false}, "group_id": {"name": "_group_id", "type": TYPE_STRING, "required": false}, "lang_tag": {"name": "_lang_tag", "type": TYPE_STRING, "required": false}, "name": {"name": "_name", "type": TYPE_STRING, "required": false}, "open": {"name": "_open", "type": TYPE_BOOL, "required": false}, } # Avatar URL. var _avatar_url var avatar_url : String: get: return "" if not _avatar_url is String else String(_avatar_url) # Description string. var _description var description : String: get: return "" if not _description is String else String(_description) # The ID of the group to update. var _group_id var group_id : String: get: return "" if not _group_id is String else String(_group_id) # Lang tag. var _lang_tag var lang_tag : String: get: return "" if not _lang_tag is String else String(_lang_tag) # Name. var _name var name : String: get: return "" if not _name is String else String(_name) # Open is true if anyone should be allowed to join, or false if joins must be approved by a group admin. var _open var open : bool: get: return false if not _open is bool else bool(_open) func _init(p_exception = null): super(p_exception) static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiUpdateGroupRequest: return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiUpdateGroupRequest", p_dict), ApiUpdateGroupRequest) as ApiUpdateGroupRequest func serialize() -> Dictionary: return NakamaSerializer.serialize(self) func _to_string() -> String: if is_exception(): return get_exception()._to_string() var output : String = "" output += "avatar_url: %s, " % _avatar_url output += "description: %s, " % _description output += "group_id: %s, " % _group_id output += "lang_tag: %s, " % _lang_tag output += "name: %s, " % _name output += "open: %s, " % _open return output # A user in the server. class ApiUser extends NakamaAsyncResult: const _SCHEMA = { "apple_id": {"name": "_apple_id", "type": TYPE_STRING, "required": false}, "avatar_url": {"name": "_avatar_url", "type": TYPE_STRING, "required": false}, "create_time": {"name": "_create_time", "type": TYPE_STRING, "required": false}, "display_name": {"name": "_display_name", "type": TYPE_STRING, "required": false}, "edge_count": {"name": "_edge_count", "type": TYPE_INT, "required": false}, "facebook_id": {"name": "_facebook_id", "type": TYPE_STRING, "required": false}, "facebook_instant_game_id": {"name": "_facebook_instant_game_id", "type": TYPE_STRING, "required": false}, "gamecenter_id": {"name": "_gamecenter_id", "type": TYPE_STRING, "required": false}, "google_id": {"name": "_google_id", "type": TYPE_STRING, "required": false}, "id": {"name": "_id", "type": TYPE_STRING, "required": false}, "lang_tag": {"name": "_lang_tag", "type": TYPE_STRING, "required": false}, "location": {"name": "_location", "type": TYPE_STRING, "required": false}, "metadata": {"name": "_metadata", "type": TYPE_STRING, "required": false}, "online": {"name": "_online", "type": TYPE_BOOL, "required": false}, "steam_id": {"name": "_steam_id", "type": TYPE_STRING, "required": false}, "timezone": {"name": "_timezone", "type": TYPE_STRING, "required": false}, "update_time": {"name": "_update_time", "type": TYPE_STRING, "required": false}, "username": {"name": "_username", "type": TYPE_STRING, "required": false}, } # The Apple Sign In ID in the user's account. var _apple_id var apple_id : String: get: return "" if not _apple_id is String else String(_apple_id) # A URL for an avatar image. var _avatar_url var avatar_url : String: get: return "" if not _avatar_url is String else String(_avatar_url) # The UNIX time when the user was created. var _create_time var create_time : String: get: return "" if not _create_time is String else String(_create_time) # The display name of the user. var _display_name var display_name : String: get: return "" if not _display_name is String else String(_display_name) # Number of related edges to this user. var _edge_count var edge_count : int: get: return 0 if not _edge_count is int else int(_edge_count) # The Facebook id in the user's account. var _facebook_id var facebook_id : String: get: return "" if not _facebook_id is String else String(_facebook_id) # The Facebook Instant Game ID in the user's account. var _facebook_instant_game_id var facebook_instant_game_id : String: get: return "" if not _facebook_instant_game_id is String else String(_facebook_instant_game_id) # The Apple Game Center in of the user's account. var _gamecenter_id var gamecenter_id : String: get: return "" if not _gamecenter_id is String else String(_gamecenter_id) # The Google id in the user's account. var _google_id var google_id : String: get: return "" if not _google_id is String else String(_google_id) # The id of the user's account. var _id var id : String: get: return "" if not _id is String else String(_id) # The language expected to be a tag which follows the BCP-47 spec. var _lang_tag var lang_tag : String: get: return "" if not _lang_tag is String else String(_lang_tag) # The location set by the user. var _location var location : String: get: return "" if not _location is String else String(_location) # Additional information stored as a JSON object. var _metadata var metadata : String: get: return "" if not _metadata is String else String(_metadata) # Indicates whether the user is currently online. var _online var online : bool: get: return false if not _online is bool else bool(_online) # The Steam id in the user's account. var _steam_id var steam_id : String: get: return "" if not _steam_id is String else String(_steam_id) # The timezone set by the user. var _timezone var timezone : String: get: return "" if not _timezone is String else String(_timezone) # The UNIX time when the user was last updated. var _update_time var update_time : String: get: return "" if not _update_time is String else String(_update_time) # The username of the user's account. var _username var username : String: get: return "" if not _username is String else String(_username) func _init(p_exception = null): super(p_exception) static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiUser: return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiUser", p_dict), ApiUser) as ApiUser func serialize() -> Dictionary: return NakamaSerializer.serialize(self) func _to_string() -> String: if is_exception(): return get_exception()._to_string() var output : String = "" output += "apple_id: %s, " % _apple_id output += "avatar_url: %s, " % _avatar_url output += "create_time: %s, " % _create_time output += "display_name: %s, " % _display_name output += "edge_count: %s, " % _edge_count output += "facebook_id: %s, " % _facebook_id output += "facebook_instant_game_id: %s, " % _facebook_instant_game_id output += "gamecenter_id: %s, " % _gamecenter_id output += "google_id: %s, " % _google_id output += "id: %s, " % _id output += "lang_tag: %s, " % _lang_tag output += "location: %s, " % _location output += "metadata: %s, " % _metadata output += "online: %s, " % _online output += "steam_id: %s, " % _steam_id output += "timezone: %s, " % _timezone output += "update_time: %s, " % _update_time output += "username: %s, " % _username return output # A list of groups belonging to a user, along with the user's role in each group. class ApiUserGroupList extends NakamaAsyncResult: const _SCHEMA = { "cursor": {"name": "_cursor", "type": TYPE_STRING, "required": false}, "user_groups": {"name": "_user_groups", "type": TYPE_ARRAY, "required": false, "content": "UserGroupListUserGroup"}, } # Cursor for the next page of results, if any. var _cursor var cursor : String: get: return "" if not _cursor is String else String(_cursor) # Group-role pairs for a user. var _user_groups var user_groups : Array: get: return Array() if not _user_groups is Array else Array(_user_groups) func _init(p_exception = null): super(p_exception) static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiUserGroupList: return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiUserGroupList", p_dict), ApiUserGroupList) as ApiUserGroupList func serialize() -> Dictionary: return NakamaSerializer.serialize(self) func _to_string() -> String: if is_exception(): return get_exception()._to_string() var output : String = "" output += "cursor: %s, " % _cursor output += "user_groups: %s, " % [_user_groups] return output # A collection of zero or more users. class ApiUsers extends NakamaAsyncResult: const _SCHEMA = { "users": {"name": "_users", "type": TYPE_ARRAY, "required": false, "content": "ApiUser"}, } # The User objects. var _users var users : Array: get: return Array() if not _users is Array else Array(_users) func _init(p_exception = null): super(p_exception) static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiUsers: return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiUsers", p_dict), ApiUsers) as ApiUsers func serialize() -> Dictionary: return NakamaSerializer.serialize(self) func _to_string() -> String: if is_exception(): return get_exception()._to_string() var output : String = "" output += "users: %s, " % [_users] return output # class ApiValidatePurchaseAppleRequest extends NakamaAsyncResult: const _SCHEMA = { "persist": {"name": "_persist", "type": TYPE_BOOL, "required": false}, "receipt": {"name": "_receipt", "type": TYPE_STRING, "required": false}, } # var _persist var persist : bool: get: return false if not _persist is bool else bool(_persist) # Base64 encoded Apple receipt data payload. var _receipt var receipt : String: get: return "" if not _receipt is String else String(_receipt) func _init(p_exception = null): super(p_exception) static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiValidatePurchaseAppleRequest: return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiValidatePurchaseAppleRequest", p_dict), ApiValidatePurchaseAppleRequest) as ApiValidatePurchaseAppleRequest func serialize() -> Dictionary: return NakamaSerializer.serialize(self) func _to_string() -> String: if is_exception(): return get_exception()._to_string() var output : String = "" output += "persist: %s, " % _persist output += "receipt: %s, " % _receipt return output # class ApiValidatePurchaseGoogleRequest extends NakamaAsyncResult: const _SCHEMA = { "persist": {"name": "_persist", "type": TYPE_BOOL, "required": false}, "purchase": {"name": "_purchase", "type": TYPE_STRING, "required": false}, } # var _persist var persist : bool: get: return false if not _persist is bool else bool(_persist) # JSON encoded Google purchase payload. var _purchase var purchase : String: get: return "" if not _purchase is String else String(_purchase) func _init(p_exception = null): super(p_exception) static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiValidatePurchaseGoogleRequest: return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiValidatePurchaseGoogleRequest", p_dict), ApiValidatePurchaseGoogleRequest) as ApiValidatePurchaseGoogleRequest func serialize() -> Dictionary: return NakamaSerializer.serialize(self) func _to_string() -> String: if is_exception(): return get_exception()._to_string() var output : String = "" output += "persist: %s, " % _persist output += "purchase: %s, " % _purchase return output # class ApiValidatePurchaseHuaweiRequest extends NakamaAsyncResult: const _SCHEMA = { "persist": {"name": "_persist", "type": TYPE_BOOL, "required": false}, "purchase": {"name": "_purchase", "type": TYPE_STRING, "required": false}, "signature": {"name": "_signature", "type": TYPE_STRING, "required": false}, } # var _persist var persist : bool: get: return false if not _persist is bool else bool(_persist) # JSON encoded Huawei InAppPurchaseData. var _purchase var purchase : String: get: return "" if not _purchase is String else String(_purchase) # InAppPurchaseData signature. var _signature var signature : String: get: return "" if not _signature is String else String(_signature) func _init(p_exception = null): super(p_exception) static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiValidatePurchaseHuaweiRequest: return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiValidatePurchaseHuaweiRequest", p_dict), ApiValidatePurchaseHuaweiRequest) as ApiValidatePurchaseHuaweiRequest func serialize() -> Dictionary: return NakamaSerializer.serialize(self) func _to_string() -> String: if is_exception(): return get_exception()._to_string() var output : String = "" output += "persist: %s, " % _persist output += "purchase: %s, " % _purchase output += "signature: %s, " % _signature return output # Validate IAP response. class ApiValidatePurchaseResponse extends NakamaAsyncResult: const _SCHEMA = { "validated_purchases": {"name": "_validated_purchases", "type": TYPE_ARRAY, "required": false, "content": "ApiValidatedPurchase"}, } # Newly seen validated purchases. var _validated_purchases var validated_purchases : Array: get: return Array() if not _validated_purchases is Array else Array(_validated_purchases) func _init(p_exception = null): super(p_exception) static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiValidatePurchaseResponse: return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiValidatePurchaseResponse", p_dict), ApiValidatePurchaseResponse) as ApiValidatePurchaseResponse func serialize() -> Dictionary: return NakamaSerializer.serialize(self) func _to_string() -> String: if is_exception(): return get_exception()._to_string() var output : String = "" output += "validated_purchases: %s, " % [_validated_purchases] return output # class ApiValidateSubscriptionAppleRequest extends NakamaAsyncResult: const _SCHEMA = { "persist": {"name": "_persist", "type": TYPE_BOOL, "required": false}, "receipt": {"name": "_receipt", "type": TYPE_STRING, "required": false}, } # Persist the subscription. var _persist var persist : bool: get: return false if not _persist is bool else bool(_persist) # Base64 encoded Apple receipt data payload. var _receipt var receipt : String: get: return "" if not _receipt is String else String(_receipt) func _init(p_exception = null): super(p_exception) static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiValidateSubscriptionAppleRequest: return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiValidateSubscriptionAppleRequest", p_dict), ApiValidateSubscriptionAppleRequest) as ApiValidateSubscriptionAppleRequest func serialize() -> Dictionary: return NakamaSerializer.serialize(self) func _to_string() -> String: if is_exception(): return get_exception()._to_string() var output : String = "" output += "persist: %s, " % _persist output += "receipt: %s, " % _receipt return output # class ApiValidateSubscriptionGoogleRequest extends NakamaAsyncResult: const _SCHEMA = { "persist": {"name": "_persist", "type": TYPE_BOOL, "required": false}, "receipt": {"name": "_receipt", "type": TYPE_STRING, "required": false}, } # Persist the subscription. var _persist var persist : bool: get: return false if not _persist is bool else bool(_persist) # JSON encoded Google purchase payload. var _receipt var receipt : String: get: return "" if not _receipt is String else String(_receipt) func _init(p_exception = null): super(p_exception) static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiValidateSubscriptionGoogleRequest: return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiValidateSubscriptionGoogleRequest", p_dict), ApiValidateSubscriptionGoogleRequest) as ApiValidateSubscriptionGoogleRequest func serialize() -> Dictionary: return NakamaSerializer.serialize(self) func _to_string() -> String: if is_exception(): return get_exception()._to_string() var output : String = "" output += "persist: %s, " % _persist output += "receipt: %s, " % _receipt return output # Validate Subscription response. class ApiValidateSubscriptionResponse extends NakamaAsyncResult: const _SCHEMA = { "validated_subscription": {"name": "_validated_subscription", "type": "ApiValidatedSubscription", "required": false}, } # var _validated_subscription var validated_subscription : ApiValidatedSubscription: get: return _validated_subscription as ApiValidatedSubscription func _init(p_exception = null): super(p_exception) static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiValidateSubscriptionResponse: return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiValidateSubscriptionResponse", p_dict), ApiValidateSubscriptionResponse) as ApiValidateSubscriptionResponse func serialize() -> Dictionary: return NakamaSerializer.serialize(self) func _to_string() -> String: if is_exception(): return get_exception()._to_string() var output : String = "" output += "validated_subscription: %s, " % _validated_subscription return output # Validated Purchase stored by Nakama. class ApiValidatedPurchase extends NakamaAsyncResult: const _SCHEMA = { "create_time": {"name": "_create_time", "type": TYPE_STRING, "required": false}, "environment": {"name": "_environment", "type": TYPE_INT, "required": false}, "product_id": {"name": "_product_id", "type": TYPE_STRING, "required": false}, "provider_response": {"name": "_provider_response", "type": TYPE_STRING, "required": false}, "purchase_time": {"name": "_purchase_time", "type": TYPE_STRING, "required": false}, "refund_time": {"name": "_refund_time", "type": TYPE_STRING, "required": false}, "seen_before": {"name": "_seen_before", "type": TYPE_BOOL, "required": false}, "store": {"name": "_store", "type": TYPE_INT, "required": false}, "transaction_id": {"name": "_transaction_id", "type": TYPE_STRING, "required": false}, "update_time": {"name": "_update_time", "type": TYPE_STRING, "required": false}, "user_id": {"name": "_user_id", "type": TYPE_STRING, "required": false}, } # Timestamp when the receipt validation was stored in DB. var _create_time var create_time : String: get: return "" if not _create_time is String else String(_create_time) # Whether the purchase was done in production or sandbox environment. var _environment var environment : int: get: return ApiStoreEnvironment.values()[0] if not ApiStoreEnvironment.values().has(_environment) else _environment # Purchase Product ID. var _product_id var product_id : String: get: return "" if not _product_id is String else String(_product_id) # Raw provider validation response. var _provider_response var provider_response : String: get: return "" if not _provider_response is String else String(_provider_response) # Timestamp when the purchase was done. var _purchase_time var purchase_time : String: get: return "" if not _purchase_time is String else String(_purchase_time) # var _refund_time var refund_time : String: get: return "" if not _refund_time is String else String(_refund_time) # Whether the purchase had already been validated by Nakama before. var _seen_before var seen_before : bool: get: return false if not _seen_before is bool else bool(_seen_before) # var _store var store : int: get: return ApiStoreProvider.values()[0] if not ApiStoreProvider.values().has(_store) else _store # Purchase Transaction ID. var _transaction_id var transaction_id : String: get: return "" if not _transaction_id is String else String(_transaction_id) # Timestamp when the receipt validation was updated in DB. var _update_time var update_time : String: get: return "" if not _update_time is String else String(_update_time) # Purchase User ID. var _user_id var user_id : String: get: return "" if not _user_id is String else String(_user_id) func _init(p_exception = null): super(p_exception) static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiValidatedPurchase: return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiValidatedPurchase", p_dict), ApiValidatedPurchase) as ApiValidatedPurchase func serialize() -> Dictionary: return NakamaSerializer.serialize(self) func _to_string() -> String: if is_exception(): return get_exception()._to_string() var output : String = "" output += "create_time: %s, " % _create_time output += "environment: %s, " % _environment output += "product_id: %s, " % _product_id output += "provider_response: %s, " % _provider_response output += "purchase_time: %s, " % _purchase_time output += "refund_time: %s, " % _refund_time output += "seen_before: %s, " % _seen_before output += "store: %s, " % _store output += "transaction_id: %s, " % _transaction_id output += "update_time: %s, " % _update_time output += "user_id: %s, " % _user_id return output # class ApiValidatedSubscription extends NakamaAsyncResult: const _SCHEMA = { "active": {"name": "_active", "type": TYPE_BOOL, "required": false}, "create_time": {"name": "_create_time", "type": TYPE_STRING, "required": false}, "environment": {"name": "_environment", "type": TYPE_INT, "required": false}, "expiry_time": {"name": "_expiry_time", "type": TYPE_STRING, "required": false}, "original_transaction_id": {"name": "_original_transaction_id", "type": TYPE_STRING, "required": false}, "product_id": {"name": "_product_id", "type": TYPE_STRING, "required": false}, "provider_notification": {"name": "_provider_notification", "type": TYPE_STRING, "required": false}, "provider_response": {"name": "_provider_response", "type": TYPE_STRING, "required": false}, "purchase_time": {"name": "_purchase_time", "type": TYPE_STRING, "required": false}, "refund_time": {"name": "_refund_time", "type": TYPE_STRING, "required": false}, "store": {"name": "_store", "type": TYPE_INT, "required": false}, "update_time": {"name": "_update_time", "type": TYPE_STRING, "required": false}, "user_id": {"name": "_user_id", "type": TYPE_STRING, "required": false}, } # Whether the subscription is currently active or not. var _active var active : bool: get: return false if not _active is bool else bool(_active) # UNIX Timestamp when the receipt validation was stored in DB. var _create_time var create_time : String: get: return "" if not _create_time is String else String(_create_time) # Whether the purchase was done in production or sandbox environment. var _environment var environment : int: get: return ApiStoreEnvironment.values()[0] if not ApiStoreEnvironment.values().has(_environment) else _environment # Subscription expiration time. The subscription can still be auto-renewed to extend the expiration time further. var _expiry_time var expiry_time : String: get: return "" if not _expiry_time is String else String(_expiry_time) # Purchase Original transaction ID (we only keep track of the original subscription, not subsequent renewals). var _original_transaction_id var original_transaction_id : String: get: return "" if not _original_transaction_id is String else String(_original_transaction_id) # Purchase Product ID. var _product_id var product_id : String: get: return "" if not _product_id is String else String(_product_id) # Raw provider notification body. var _provider_notification var provider_notification : String: get: return "" if not _provider_notification is String else String(_provider_notification) # Raw provider validation response body. var _provider_response var provider_response : String: get: return "" if not _provider_response is String else String(_provider_response) # UNIX Timestamp when the purchase was done. var _purchase_time var purchase_time : String: get: return "" if not _purchase_time is String else String(_purchase_time) # Subscription refund time. If this time is set, the subscription was refunded. var _refund_time var refund_time : String: get: return "" if not _refund_time is String else String(_refund_time) # var _store var store : int: get: return ApiStoreProvider.values()[0] if not ApiStoreProvider.values().has(_store) else _store # UNIX Timestamp when the receipt validation was updated in DB. var _update_time var update_time : String: get: return "" if not _update_time is String else String(_update_time) # Subscription User ID. var _user_id var user_id : String: get: return "" if not _user_id is String else String(_user_id) func _init(p_exception = null): super(p_exception) static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiValidatedSubscription: return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiValidatedSubscription", p_dict), ApiValidatedSubscription) as ApiValidatedSubscription func serialize() -> Dictionary: return NakamaSerializer.serialize(self) func _to_string() -> String: if is_exception(): return get_exception()._to_string() var output : String = "" output += "active: %s, " % _active output += "create_time: %s, " % _create_time output += "environment: %s, " % _environment output += "expiry_time: %s, " % _expiry_time output += "original_transaction_id: %s, " % _original_transaction_id output += "product_id: %s, " % _product_id output += "provider_notification: %s, " % _provider_notification output += "provider_response: %s, " % _provider_response output += "purchase_time: %s, " % _purchase_time output += "refund_time: %s, " % _refund_time output += "store: %s, " % _store output += "update_time: %s, " % _update_time output += "user_id: %s, " % _user_id return output # The object to store. class ApiWriteStorageObject extends NakamaAsyncResult: const _SCHEMA = { "collection": {"name": "_collection", "type": TYPE_STRING, "required": false}, "key": {"name": "_key", "type": TYPE_STRING, "required": false}, "permission_read": {"name": "_permission_read", "type": TYPE_INT, "required": false}, "permission_write": {"name": "_permission_write", "type": TYPE_INT, "required": false}, "value": {"name": "_value", "type": TYPE_STRING, "required": false}, "version": {"name": "_version", "type": TYPE_STRING, "required": false}, } # The collection to store the object. var _collection var collection : String: get: return "" if not _collection is String else String(_collection) # The key for the object within the collection. var _key var key : String: get: return "" if not _key is String else String(_key) # The read access permissions for the object. var _permission_read var permission_read : int: get: return 0 if not _permission_read is int else int(_permission_read) # The write access permissions for the object. var _permission_write var permission_write : int: get: return 0 if not _permission_write is int else int(_permission_write) # The value of the object. var _value var value : String: get: return "" if not _value is String else String(_value) # The version hash of the object to check. Possible values are: ["", "*", "#hash#"]. var _version var version : String: get: return "" if not _version is String else String(_version) func _init(p_exception = null): super(p_exception) static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiWriteStorageObject: return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiWriteStorageObject", p_dict), ApiWriteStorageObject) as ApiWriteStorageObject func serialize() -> Dictionary: return NakamaSerializer.serialize(self) func _to_string() -> String: if is_exception(): return get_exception()._to_string() var output : String = "" output += "collection: %s, " % _collection output += "key: %s, " % _key output += "permission_read: %s, " % _permission_read output += "permission_write: %s, " % _permission_write output += "value: %s, " % _value output += "version: %s, " % _version return output # Write objects to the storage engine. class ApiWriteStorageObjectsRequest extends NakamaAsyncResult: const _SCHEMA = { "objects": {"name": "_objects", "type": TYPE_ARRAY, "required": false, "content": "ApiWriteStorageObject"}, } # The objects to store on the server. var _objects var objects : Array: get: return Array() if not _objects is Array else Array(_objects) func _init(p_exception = null): super(p_exception) static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiWriteStorageObjectsRequest: return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiWriteStorageObjectsRequest", p_dict), ApiWriteStorageObjectsRequest) as ApiWriteStorageObjectsRequest func serialize() -> Dictionary: return NakamaSerializer.serialize(self) func _to_string() -> String: if is_exception(): return get_exception()._to_string() var output : String = "" output += "objects: %s, " % [_objects] return output # class ProtobufAny extends NakamaAsyncResult: const _SCHEMA = { "type_url": {"name": "_type_url", "type": TYPE_STRING, "required": false}, "value": {"name": "_value", "type": TYPE_STRING, "required": false}, } # var _type_url var type_url : String: get: return "" if not _type_url is String else String(_type_url) # var _value var value : String: get: return "" if not _value is String else String(_value) func _init(p_exception = null): super(p_exception) static func create(p_ns : GDScript, p_dict : Dictionary) -> ProtobufAny: return _safe_ret(NakamaSerializer.deserialize(p_ns, "ProtobufAny", p_dict), ProtobufAny) as ProtobufAny func serialize() -> Dictionary: return NakamaSerializer.serialize(self) func _to_string() -> String: if is_exception(): return get_exception()._to_string() var output : String = "" output += "type_url: %s, " % _type_url output += "value: %s, " % _value return output # class RpcStatus extends NakamaAsyncResult: const _SCHEMA = { "code": {"name": "_code", "type": TYPE_INT, "required": false}, "details": {"name": "_details", "type": TYPE_ARRAY, "required": false, "content": "ProtobufAny"}, "message": {"name": "_message", "type": TYPE_STRING, "required": false}, } # var _code var code : int: get: return 0 if not _code is int else int(_code) # var _details var details : Array: get: return Array() if not _details is Array else Array(_details) # var _message var message : String: get: return "" if not _message is String else String(_message) func _init(p_exception = null): super(p_exception) static func create(p_ns : GDScript, p_dict : Dictionary) -> RpcStatus: return _safe_ret(NakamaSerializer.deserialize(p_ns, "RpcStatus", p_dict), RpcStatus) as RpcStatus func serialize() -> Dictionary: return NakamaSerializer.serialize(self) func _to_string() -> String: if is_exception(): return get_exception()._to_string() var output : String = "" output += "code: %s, " % _code output += "details: %s, " % [_details] output += "message: %s, " % _message return output # The low level client for the Nakama API. class ApiClient extends RefCounted: var _base_uri : String var _http_adapter var _namespace : GDScript var _server_key : String var auto_refresh := true var auto_refresh_time := 300 var auto_retry : bool: set(p_value): _http_adapter.auto_retry = p_value get: return _http_adapter.auto_retry var auto_retry_count : int: set(p_value): _http_adapter.auto_retry_count = p_value get: return _http_adapter.auto_retry_count var auto_retry_backoff_base : int: set(p_value): _http_adapter.auto_retry_backoff_base = p_value get: return _http_adapter.auto_retry_backoff_base var last_cancel_token: get: return _http_adapter.get_last_token() func _init(p_base_uri : String, p_http_adapter, p_namespace : GDScript, p_server_key : String, p_timeout : int = 10): _base_uri = p_base_uri _http_adapter = p_http_adapter _http_adapter.timeout = p_timeout _namespace = p_namespace _server_key = p_server_key func _refresh_session(p_session : NakamaSession): if auto_refresh and p_session.is_valid() and p_session.refresh_token and not p_session.is_refresh_expired() and p_session.would_expire_in(auto_refresh_time): var request = ApiSessionRefreshRequest.new() request._token = p_session.refresh_token return await session_refresh_async(_server_key, "", request) return null func cancel_request(p_token): if p_token: _http_adapter.cancel_request(p_token) # A healthcheck which load balancers can use to check the service. func healthcheck_async( p_session : NakamaSession ) -> NakamaAsyncResult: var try_refresh = await _refresh_session(p_session) if try_refresh != null: if try_refresh.is_exception(): return NakamaAsyncResult.new(try_refresh.get_exception()) await p_session.refresh(try_refresh) var urlpath : String = "/healthcheck" var query_params = "" var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""] var method = "GET" var headers = {} var header = "Bearer %s" % p_session.token headers["Authorization"] = header var content : PackedByteArray var result = await _http_adapter.send_async(method, uri, headers, content) if result is NakamaException: return NakamaAsyncResult.new(result) return NakamaAsyncResult.new() # Delete the current user's account. func delete_account_async( p_session : NakamaSession ) -> NakamaAsyncResult: var try_refresh = await _refresh_session(p_session) if try_refresh != null: if try_refresh.is_exception(): return NakamaAsyncResult.new(try_refresh.get_exception()) await p_session.refresh(try_refresh) var urlpath : String = "/v2/account" var query_params = "" var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""] var method = "DELETE" var headers = {} var header = "Bearer %s" % p_session.token headers["Authorization"] = header var content : PackedByteArray var result = await _http_adapter.send_async(method, uri, headers, content) if result is NakamaException: return NakamaAsyncResult.new(result) return NakamaAsyncResult.new() # Fetch the current user's account. func get_account_async( p_session : NakamaSession ) -> ApiAccount: var try_refresh = await _refresh_session(p_session) if try_refresh != null: if try_refresh.is_exception(): return ApiAccount.new(try_refresh.get_exception()) await p_session.refresh(try_refresh) var urlpath : String = "/v2/account" var query_params = "" var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""] var method = "GET" var headers = {} var header = "Bearer %s" % p_session.token headers["Authorization"] = header var content : PackedByteArray var result = await _http_adapter.send_async(method, uri, headers, content) if result is NakamaException: return ApiAccount.new(result) var out : ApiAccount = NakamaSerializer.deserialize(_namespace, "ApiAccount", result) return out # Update fields in the current user's account. func update_account_async( p_session : NakamaSession , p_body : ApiUpdateAccountRequest ) -> NakamaAsyncResult: var try_refresh = await _refresh_session(p_session) if try_refresh != null: if try_refresh.is_exception(): return NakamaAsyncResult.new(try_refresh.get_exception()) await p_session.refresh(try_refresh) var urlpath : String = "/v2/account" var query_params = "" var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""] var method = "PUT" var headers = {} var header = "Bearer %s" % p_session.token headers["Authorization"] = header var content : PackedByteArray content = JSON.stringify(p_body.serialize()).to_utf8_buffer() var result = await _http_adapter.send_async(method, uri, headers, content) if result is NakamaException: return NakamaAsyncResult.new(result) return NakamaAsyncResult.new() # Authenticate a user with an Apple ID against the server. func authenticate_apple_async( p_basic_auth_username : String , p_basic_auth_password : String , p_account : ApiAccountApple , p_create = null # : boolean , p_username = null # : string ) -> ApiSession: var urlpath : String = "/v2/account/authenticate/apple" var query_params = "" if p_create != null: query_params += "create=%s&" % str(bool(p_create)).to_lower() if p_username != null: query_params += "username=%s&" % NakamaSerializer.escape_http(p_username) var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""] var method = "POST" var headers = {} var credentials = Marshalls.utf8_to_base64(p_basic_auth_username + ":" + p_basic_auth_password) var header = "Basic %s" % credentials headers["Authorization"] = header var content : PackedByteArray content = JSON.stringify(p_account.serialize()).to_utf8_buffer() var result = await _http_adapter.send_async(method, uri, headers, content) if result is NakamaException: return ApiSession.new(result) var out : ApiSession = NakamaSerializer.deserialize(_namespace, "ApiSession", result) return out # Authenticate a user with a custom id against the server. func authenticate_custom_async( p_basic_auth_username : String , p_basic_auth_password : String , p_account : ApiAccountCustom , p_create = null # : boolean , p_username = null # : string ) -> ApiSession: var urlpath : String = "/v2/account/authenticate/custom" var query_params = "" if p_create != null: query_params += "create=%s&" % str(bool(p_create)).to_lower() if p_username != null: query_params += "username=%s&" % NakamaSerializer.escape_http(p_username) var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""] var method = "POST" var headers = {} var credentials = Marshalls.utf8_to_base64(p_basic_auth_username + ":" + p_basic_auth_password) var header = "Basic %s" % credentials headers["Authorization"] = header var content : PackedByteArray content = JSON.stringify(p_account.serialize()).to_utf8_buffer() var result = await _http_adapter.send_async(method, uri, headers, content) if result is NakamaException: return ApiSession.new(result) var out : ApiSession = NakamaSerializer.deserialize(_namespace, "ApiSession", result) return out # Authenticate a user with a device id against the server. func authenticate_device_async( p_basic_auth_username : String , p_basic_auth_password : String , p_account : ApiAccountDevice , p_create = null # : boolean , p_username = null # : string ) -> ApiSession: var urlpath : String = "/v2/account/authenticate/device" var query_params = "" if p_create != null: query_params += "create=%s&" % str(bool(p_create)).to_lower() if p_username != null: query_params += "username=%s&" % NakamaSerializer.escape_http(p_username) var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""] var method = "POST" var headers = {} var credentials = Marshalls.utf8_to_base64(p_basic_auth_username + ":" + p_basic_auth_password) var header = "Basic %s" % credentials headers["Authorization"] = header var content : PackedByteArray content = JSON.stringify(p_account.serialize()).to_utf8_buffer() var result = await _http_adapter.send_async(method, uri, headers, content) if result is NakamaException: return ApiSession.new(result) var out : ApiSession = NakamaSerializer.deserialize(_namespace, "ApiSession", result) return out # Authenticate a user with an email+password against the server. func authenticate_email_async( p_basic_auth_username : String , p_basic_auth_password : String , p_account : ApiAccountEmail , p_create = null # : boolean , p_username = null # : string ) -> ApiSession: var urlpath : String = "/v2/account/authenticate/email" var query_params = "" if p_create != null: query_params += "create=%s&" % str(bool(p_create)).to_lower() if p_username != null: query_params += "username=%s&" % NakamaSerializer.escape_http(p_username) var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""] var method = "POST" var headers = {} var credentials = Marshalls.utf8_to_base64(p_basic_auth_username + ":" + p_basic_auth_password) var header = "Basic %s" % credentials headers["Authorization"] = header var content : PackedByteArray content = JSON.stringify(p_account.serialize()).to_utf8_buffer() var result = await _http_adapter.send_async(method, uri, headers, content) if result is NakamaException: return ApiSession.new(result) var out : ApiSession = NakamaSerializer.deserialize(_namespace, "ApiSession", result) return out # Authenticate a user with a Facebook OAuth token against the server. func authenticate_facebook_async( p_basic_auth_username : String , p_basic_auth_password : String , p_account : ApiAccountFacebook , p_create = null # : boolean , p_username = null # : string , p_sync = null # : boolean ) -> ApiSession: var urlpath : String = "/v2/account/authenticate/facebook" var query_params = "" if p_create != null: query_params += "create=%s&" % str(bool(p_create)).to_lower() if p_username != null: query_params += "username=%s&" % NakamaSerializer.escape_http(p_username) if p_sync != null: query_params += "sync=%s&" % str(bool(p_sync)).to_lower() var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""] var method = "POST" var headers = {} var credentials = Marshalls.utf8_to_base64(p_basic_auth_username + ":" + p_basic_auth_password) var header = "Basic %s" % credentials headers["Authorization"] = header var content : PackedByteArray content = JSON.stringify(p_account.serialize()).to_utf8_buffer() var result = await _http_adapter.send_async(method, uri, headers, content) if result is NakamaException: return ApiSession.new(result) var out : ApiSession = NakamaSerializer.deserialize(_namespace, "ApiSession", result) return out # Authenticate a user with a Facebook Instant Game token against the server. func authenticate_facebook_instant_game_async( p_basic_auth_username : String , p_basic_auth_password : String , p_account : ApiAccountFacebookInstantGame , p_create = null # : boolean , p_username = null # : string ) -> ApiSession: var urlpath : String = "/v2/account/authenticate/facebookinstantgame" var query_params = "" if p_create != null: query_params += "create=%s&" % str(bool(p_create)).to_lower() if p_username != null: query_params += "username=%s&" % NakamaSerializer.escape_http(p_username) var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""] var method = "POST" var headers = {} var credentials = Marshalls.utf8_to_base64(p_basic_auth_username + ":" + p_basic_auth_password) var header = "Basic %s" % credentials headers["Authorization"] = header var content : PackedByteArray content = JSON.stringify(p_account.serialize()).to_utf8_buffer() var result = await _http_adapter.send_async(method, uri, headers, content) if result is NakamaException: return ApiSession.new(result) var out : ApiSession = NakamaSerializer.deserialize(_namespace, "ApiSession", result) return out # Authenticate a user with Apple's GameCenter against the server. func authenticate_game_center_async( p_basic_auth_username : String , p_basic_auth_password : String , p_account : ApiAccountGameCenter , p_create = null # : boolean , p_username = null # : string ) -> ApiSession: var urlpath : String = "/v2/account/authenticate/gamecenter" var query_params = "" if p_create != null: query_params += "create=%s&" % str(bool(p_create)).to_lower() if p_username != null: query_params += "username=%s&" % NakamaSerializer.escape_http(p_username) var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""] var method = "POST" var headers = {} var credentials = Marshalls.utf8_to_base64(p_basic_auth_username + ":" + p_basic_auth_password) var header = "Basic %s" % credentials headers["Authorization"] = header var content : PackedByteArray content = JSON.stringify(p_account.serialize()).to_utf8_buffer() var result = await _http_adapter.send_async(method, uri, headers, content) if result is NakamaException: return ApiSession.new(result) var out : ApiSession = NakamaSerializer.deserialize(_namespace, "ApiSession", result) return out # Authenticate a user with Google against the server. func authenticate_google_async( p_basic_auth_username : String , p_basic_auth_password : String , p_account : ApiAccountGoogle , p_create = null # : boolean , p_username = null # : string ) -> ApiSession: var urlpath : String = "/v2/account/authenticate/google" var query_params = "" if p_create != null: query_params += "create=%s&" % str(bool(p_create)).to_lower() if p_username != null: query_params += "username=%s&" % NakamaSerializer.escape_http(p_username) var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""] var method = "POST" var headers = {} var credentials = Marshalls.utf8_to_base64(p_basic_auth_username + ":" + p_basic_auth_password) var header = "Basic %s" % credentials headers["Authorization"] = header var content : PackedByteArray content = JSON.stringify(p_account.serialize()).to_utf8_buffer() var result = await _http_adapter.send_async(method, uri, headers, content) if result is NakamaException: return ApiSession.new(result) var out : ApiSession = NakamaSerializer.deserialize(_namespace, "ApiSession", result) return out # Authenticate a user with Steam against the server. func authenticate_steam_async( p_basic_auth_username : String , p_basic_auth_password : String , p_account : ApiAccountSteam , p_create = null # : boolean , p_username = null # : string , p_sync = null # : boolean ) -> ApiSession: var urlpath : String = "/v2/account/authenticate/steam" var query_params = "" if p_create != null: query_params += "create=%s&" % str(bool(p_create)).to_lower() if p_username != null: query_params += "username=%s&" % NakamaSerializer.escape_http(p_username) if p_sync != null: query_params += "sync=%s&" % str(bool(p_sync)).to_lower() var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""] var method = "POST" var headers = {} var credentials = Marshalls.utf8_to_base64(p_basic_auth_username + ":" + p_basic_auth_password) var header = "Basic %s" % credentials headers["Authorization"] = header var content : PackedByteArray content = JSON.stringify(p_account.serialize()).to_utf8_buffer() var result = await _http_adapter.send_async(method, uri, headers, content) if result is NakamaException: return ApiSession.new(result) var out : ApiSession = NakamaSerializer.deserialize(_namespace, "ApiSession", result) return out # Add an Apple ID to the social profiles on the current user's account. func link_apple_async( p_session : NakamaSession , p_body : ApiAccountApple ) -> NakamaAsyncResult: var try_refresh = await _refresh_session(p_session) if try_refresh != null: if try_refresh.is_exception(): return NakamaAsyncResult.new(try_refresh.get_exception()) await p_session.refresh(try_refresh) var urlpath : String = "/v2/account/link/apple" var query_params = "" var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""] var method = "POST" var headers = {} var header = "Bearer %s" % p_session.token headers["Authorization"] = header var content : PackedByteArray content = JSON.stringify(p_body.serialize()).to_utf8_buffer() var result = await _http_adapter.send_async(method, uri, headers, content) if result is NakamaException: return NakamaAsyncResult.new(result) return NakamaAsyncResult.new() # Add a custom ID to the social profiles on the current user's account. func link_custom_async( p_session : NakamaSession , p_body : ApiAccountCustom ) -> NakamaAsyncResult: var try_refresh = await _refresh_session(p_session) if try_refresh != null: if try_refresh.is_exception(): return NakamaAsyncResult.new(try_refresh.get_exception()) await p_session.refresh(try_refresh) var urlpath : String = "/v2/account/link/custom" var query_params = "" var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""] var method = "POST" var headers = {} var header = "Bearer %s" % p_session.token headers["Authorization"] = header var content : PackedByteArray content = JSON.stringify(p_body.serialize()).to_utf8_buffer() var result = await _http_adapter.send_async(method, uri, headers, content) if result is NakamaException: return NakamaAsyncResult.new(result) return NakamaAsyncResult.new() # Add a device ID to the social profiles on the current user's account. func link_device_async( p_session : NakamaSession , p_body : ApiAccountDevice ) -> NakamaAsyncResult: var try_refresh = await _refresh_session(p_session) if try_refresh != null: if try_refresh.is_exception(): return NakamaAsyncResult.new(try_refresh.get_exception()) await p_session.refresh(try_refresh) var urlpath : String = "/v2/account/link/device" var query_params = "" var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""] var method = "POST" var headers = {} var header = "Bearer %s" % p_session.token headers["Authorization"] = header var content : PackedByteArray content = JSON.stringify(p_body.serialize()).to_utf8_buffer() var result = await _http_adapter.send_async(method, uri, headers, content) if result is NakamaException: return NakamaAsyncResult.new(result) return NakamaAsyncResult.new() # Add an email+password to the social profiles on the current user's account. func link_email_async( p_session : NakamaSession , p_body : ApiAccountEmail ) -> NakamaAsyncResult: var try_refresh = await _refresh_session(p_session) if try_refresh != null: if try_refresh.is_exception(): return NakamaAsyncResult.new(try_refresh.get_exception()) await p_session.refresh(try_refresh) var urlpath : String = "/v2/account/link/email" var query_params = "" var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""] var method = "POST" var headers = {} var header = "Bearer %s" % p_session.token headers["Authorization"] = header var content : PackedByteArray content = JSON.stringify(p_body.serialize()).to_utf8_buffer() var result = await _http_adapter.send_async(method, uri, headers, content) if result is NakamaException: return NakamaAsyncResult.new(result) return NakamaAsyncResult.new() # Add Facebook to the social profiles on the current user's account. func link_facebook_async( p_session : NakamaSession , p_account : ApiAccountFacebook , p_sync = null # : boolean ) -> NakamaAsyncResult: var try_refresh = await _refresh_session(p_session) if try_refresh != null: if try_refresh.is_exception(): return NakamaAsyncResult.new(try_refresh.get_exception()) await p_session.refresh(try_refresh) var urlpath : String = "/v2/account/link/facebook" var query_params = "" if p_sync != null: query_params += "sync=%s&" % str(bool(p_sync)).to_lower() var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""] var method = "POST" var headers = {} var header = "Bearer %s" % p_session.token headers["Authorization"] = header var content : PackedByteArray content = JSON.stringify(p_account.serialize()).to_utf8_buffer() var result = await _http_adapter.send_async(method, uri, headers, content) if result is NakamaException: return NakamaAsyncResult.new(result) return NakamaAsyncResult.new() # Add Facebook Instant Game to the social profiles on the current user's account. func link_facebook_instant_game_async( p_session : NakamaSession , p_body : ApiAccountFacebookInstantGame ) -> NakamaAsyncResult: var try_refresh = await _refresh_session(p_session) if try_refresh != null: if try_refresh.is_exception(): return NakamaAsyncResult.new(try_refresh.get_exception()) await p_session.refresh(try_refresh) var urlpath : String = "/v2/account/link/facebookinstantgame" var query_params = "" var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""] var method = "POST" var headers = {} var header = "Bearer %s" % p_session.token headers["Authorization"] = header var content : PackedByteArray content = JSON.stringify(p_body.serialize()).to_utf8_buffer() var result = await _http_adapter.send_async(method, uri, headers, content) if result is NakamaException: return NakamaAsyncResult.new(result) return NakamaAsyncResult.new() # Add Apple's GameCenter to the social profiles on the current user's account. func link_game_center_async( p_session : NakamaSession , p_body : ApiAccountGameCenter ) -> NakamaAsyncResult: var try_refresh = await _refresh_session(p_session) if try_refresh != null: if try_refresh.is_exception(): return NakamaAsyncResult.new(try_refresh.get_exception()) await p_session.refresh(try_refresh) var urlpath : String = "/v2/account/link/gamecenter" var query_params = "" var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""] var method = "POST" var headers = {} var header = "Bearer %s" % p_session.token headers["Authorization"] = header var content : PackedByteArray content = JSON.stringify(p_body.serialize()).to_utf8_buffer() var result = await _http_adapter.send_async(method, uri, headers, content) if result is NakamaException: return NakamaAsyncResult.new(result) return NakamaAsyncResult.new() # Add Google to the social profiles on the current user's account. func link_google_async( p_session : NakamaSession , p_body : ApiAccountGoogle ) -> NakamaAsyncResult: var try_refresh = await _refresh_session(p_session) if try_refresh != null: if try_refresh.is_exception(): return NakamaAsyncResult.new(try_refresh.get_exception()) await p_session.refresh(try_refresh) var urlpath : String = "/v2/account/link/google" var query_params = "" var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""] var method = "POST" var headers = {} var header = "Bearer %s" % p_session.token headers["Authorization"] = header var content : PackedByteArray content = JSON.stringify(p_body.serialize()).to_utf8_buffer() var result = await _http_adapter.send_async(method, uri, headers, content) if result is NakamaException: return NakamaAsyncResult.new(result) return NakamaAsyncResult.new() # Add Steam to the social profiles on the current user's account. func link_steam_async( p_session : NakamaSession , p_body : ApiLinkSteamRequest ) -> NakamaAsyncResult: var try_refresh = await _refresh_session(p_session) if try_refresh != null: if try_refresh.is_exception(): return NakamaAsyncResult.new(try_refresh.get_exception()) await p_session.refresh(try_refresh) var urlpath : String = "/v2/account/link/steam" var query_params = "" var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""] var method = "POST" var headers = {} var header = "Bearer %s" % p_session.token headers["Authorization"] = header var content : PackedByteArray content = JSON.stringify(p_body.serialize()).to_utf8_buffer() var result = await _http_adapter.send_async(method, uri, headers, content) if result is NakamaException: return NakamaAsyncResult.new(result) return NakamaAsyncResult.new() # Refresh a user's session using a refresh token retrieved from a previous authentication request. func session_refresh_async( p_basic_auth_username : String , p_basic_auth_password : String , p_body : ApiSessionRefreshRequest ) -> ApiSession: var urlpath : String = "/v2/account/session/refresh" var query_params = "" var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""] var method = "POST" var headers = {} var credentials = Marshalls.utf8_to_base64(p_basic_auth_username + ":" + p_basic_auth_password) var header = "Basic %s" % credentials headers["Authorization"] = header var content : PackedByteArray content = JSON.stringify(p_body.serialize()).to_utf8_buffer() var result = await _http_adapter.send_async(method, uri, headers, content) if result is NakamaException: return ApiSession.new(result) var out : ApiSession = NakamaSerializer.deserialize(_namespace, "ApiSession", result) return out # Remove the Apple ID from the social profiles on the current user's account. func unlink_apple_async( p_session : NakamaSession , p_body : ApiAccountApple ) -> NakamaAsyncResult: var try_refresh = await _refresh_session(p_session) if try_refresh != null: if try_refresh.is_exception(): return NakamaAsyncResult.new(try_refresh.get_exception()) await p_session.refresh(try_refresh) var urlpath : String = "/v2/account/unlink/apple" var query_params = "" var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""] var method = "POST" var headers = {} var header = "Bearer %s" % p_session.token headers["Authorization"] = header var content : PackedByteArray content = JSON.stringify(p_body.serialize()).to_utf8_buffer() var result = await _http_adapter.send_async(method, uri, headers, content) if result is NakamaException: return NakamaAsyncResult.new(result) return NakamaAsyncResult.new() # Remove the custom ID from the social profiles on the current user's account. func unlink_custom_async( p_session : NakamaSession , p_body : ApiAccountCustom ) -> NakamaAsyncResult: var try_refresh = await _refresh_session(p_session) if try_refresh != null: if try_refresh.is_exception(): return NakamaAsyncResult.new(try_refresh.get_exception()) await p_session.refresh(try_refresh) var urlpath : String = "/v2/account/unlink/custom" var query_params = "" var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""] var method = "POST" var headers = {} var header = "Bearer %s" % p_session.token headers["Authorization"] = header var content : PackedByteArray content = JSON.stringify(p_body.serialize()).to_utf8_buffer() var result = await _http_adapter.send_async(method, uri, headers, content) if result is NakamaException: return NakamaAsyncResult.new(result) return NakamaAsyncResult.new() # Remove the device ID from the social profiles on the current user's account. func unlink_device_async( p_session : NakamaSession , p_body : ApiAccountDevice ) -> NakamaAsyncResult: var try_refresh = await _refresh_session(p_session) if try_refresh != null: if try_refresh.is_exception(): return NakamaAsyncResult.new(try_refresh.get_exception()) await p_session.refresh(try_refresh) var urlpath : String = "/v2/account/unlink/device" var query_params = "" var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""] var method = "POST" var headers = {} var header = "Bearer %s" % p_session.token headers["Authorization"] = header var content : PackedByteArray content = JSON.stringify(p_body.serialize()).to_utf8_buffer() var result = await _http_adapter.send_async(method, uri, headers, content) if result is NakamaException: return NakamaAsyncResult.new(result) return NakamaAsyncResult.new() # Remove the email+password from the social profiles on the current user's account. func unlink_email_async( p_session : NakamaSession , p_body : ApiAccountEmail ) -> NakamaAsyncResult: var try_refresh = await _refresh_session(p_session) if try_refresh != null: if try_refresh.is_exception(): return NakamaAsyncResult.new(try_refresh.get_exception()) await p_session.refresh(try_refresh) var urlpath : String = "/v2/account/unlink/email" var query_params = "" var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""] var method = "POST" var headers = {} var header = "Bearer %s" % p_session.token headers["Authorization"] = header var content : PackedByteArray content = JSON.stringify(p_body.serialize()).to_utf8_buffer() var result = await _http_adapter.send_async(method, uri, headers, content) if result is NakamaException: return NakamaAsyncResult.new(result) return NakamaAsyncResult.new() # Remove Facebook from the social profiles on the current user's account. func unlink_facebook_async( p_session : NakamaSession , p_body : ApiAccountFacebook ) -> NakamaAsyncResult: var try_refresh = await _refresh_session(p_session) if try_refresh != null: if try_refresh.is_exception(): return NakamaAsyncResult.new(try_refresh.get_exception()) await p_session.refresh(try_refresh) var urlpath : String = "/v2/account/unlink/facebook" var query_params = "" var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""] var method = "POST" var headers = {} var header = "Bearer %s" % p_session.token headers["Authorization"] = header var content : PackedByteArray content = JSON.stringify(p_body.serialize()).to_utf8_buffer() var result = await _http_adapter.send_async(method, uri, headers, content) if result is NakamaException: return NakamaAsyncResult.new(result) return NakamaAsyncResult.new() # Remove Facebook Instant Game profile from the social profiles on the current user's account. func unlink_facebook_instant_game_async( p_session : NakamaSession , p_body : ApiAccountFacebookInstantGame ) -> NakamaAsyncResult: var try_refresh = await _refresh_session(p_session) if try_refresh != null: if try_refresh.is_exception(): return NakamaAsyncResult.new(try_refresh.get_exception()) await p_session.refresh(try_refresh) var urlpath : String = "/v2/account/unlink/facebookinstantgame" var query_params = "" var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""] var method = "POST" var headers = {} var header = "Bearer %s" % p_session.token headers["Authorization"] = header var content : PackedByteArray content = JSON.stringify(p_body.serialize()).to_utf8_buffer() var result = await _http_adapter.send_async(method, uri, headers, content) if result is NakamaException: return NakamaAsyncResult.new(result) return NakamaAsyncResult.new() # Remove Apple's GameCenter from the social profiles on the current user's account. func unlink_game_center_async( p_session : NakamaSession , p_body : ApiAccountGameCenter ) -> NakamaAsyncResult: var try_refresh = await _refresh_session(p_session) if try_refresh != null: if try_refresh.is_exception(): return NakamaAsyncResult.new(try_refresh.get_exception()) await p_session.refresh(try_refresh) var urlpath : String = "/v2/account/unlink/gamecenter" var query_params = "" var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""] var method = "POST" var headers = {} var header = "Bearer %s" % p_session.token headers["Authorization"] = header var content : PackedByteArray content = JSON.stringify(p_body.serialize()).to_utf8_buffer() var result = await _http_adapter.send_async(method, uri, headers, content) if result is NakamaException: return NakamaAsyncResult.new(result) return NakamaAsyncResult.new() # Remove Google from the social profiles on the current user's account. func unlink_google_async( p_session : NakamaSession , p_body : ApiAccountGoogle ) -> NakamaAsyncResult: var try_refresh = await _refresh_session(p_session) if try_refresh != null: if try_refresh.is_exception(): return NakamaAsyncResult.new(try_refresh.get_exception()) await p_session.refresh(try_refresh) var urlpath : String = "/v2/account/unlink/google" var query_params = "" var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""] var method = "POST" var headers = {} var header = "Bearer %s" % p_session.token headers["Authorization"] = header var content : PackedByteArray content = JSON.stringify(p_body.serialize()).to_utf8_buffer() var result = await _http_adapter.send_async(method, uri, headers, content) if result is NakamaException: return NakamaAsyncResult.new(result) return NakamaAsyncResult.new() # Remove Steam from the social profiles on the current user's account. func unlink_steam_async( p_session : NakamaSession , p_body : ApiAccountSteam ) -> NakamaAsyncResult: var try_refresh = await _refresh_session(p_session) if try_refresh != null: if try_refresh.is_exception(): return NakamaAsyncResult.new(try_refresh.get_exception()) await p_session.refresh(try_refresh) var urlpath : String = "/v2/account/unlink/steam" var query_params = "" var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""] var method = "POST" var headers = {} var header = "Bearer %s" % p_session.token headers["Authorization"] = header var content : PackedByteArray content = JSON.stringify(p_body.serialize()).to_utf8_buffer() var result = await _http_adapter.send_async(method, uri, headers, content) if result is NakamaException: return NakamaAsyncResult.new(result) return NakamaAsyncResult.new() # List a channel's message history. func list_channel_messages_async( p_session : NakamaSession , p_channel_id : String , p_limit = null # : integer , p_forward = null # : boolean , p_cursor = null # : string ) -> ApiChannelMessageList: var try_refresh = await _refresh_session(p_session) if try_refresh != null: if try_refresh.is_exception(): return ApiChannelMessageList.new(try_refresh.get_exception()) await p_session.refresh(try_refresh) var urlpath : String = "/v2/channel/{channelId}" urlpath = urlpath.replace("{channelId}", NakamaSerializer.escape_http(p_channel_id)) var query_params = "" if p_limit != null: query_params += "limit=%d&" % p_limit if p_forward != null: query_params += "forward=%s&" % str(bool(p_forward)).to_lower() if p_cursor != null: query_params += "cursor=%s&" % NakamaSerializer.escape_http(p_cursor) var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""] var method = "GET" var headers = {} var header = "Bearer %s" % p_session.token headers["Authorization"] = header var content : PackedByteArray var result = await _http_adapter.send_async(method, uri, headers, content) if result is NakamaException: return ApiChannelMessageList.new(result) var out : ApiChannelMessageList = NakamaSerializer.deserialize(_namespace, "ApiChannelMessageList", result) return out # Submit an event for processing in the server's registered runtime custom events handler. func event_async( p_session : NakamaSession , p_body : ApiEvent ) -> NakamaAsyncResult: var try_refresh = await _refresh_session(p_session) if try_refresh != null: if try_refresh.is_exception(): return NakamaAsyncResult.new(try_refresh.get_exception()) await p_session.refresh(try_refresh) var urlpath : String = "/v2/event" var query_params = "" var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""] var method = "POST" var headers = {} var header = "Bearer %s" % p_session.token headers["Authorization"] = header var content : PackedByteArray content = JSON.stringify(p_body.serialize()).to_utf8_buffer() var result = await _http_adapter.send_async(method, uri, headers, content) if result is NakamaException: return NakamaAsyncResult.new(result) return NakamaAsyncResult.new() # Delete one or more users by ID or username. func delete_friends_async( p_session : NakamaSession , p_ids = null # : array , p_usernames = null # : array ) -> NakamaAsyncResult: var try_refresh = await _refresh_session(p_session) if try_refresh != null: if try_refresh.is_exception(): return NakamaAsyncResult.new(try_refresh.get_exception()) await p_session.refresh(try_refresh) var urlpath : String = "/v2/friend" var query_params = "" if p_ids != null: for elem in p_ids: query_params += "ids=%s&" % elem if p_usernames != null: for elem in p_usernames: query_params += "usernames=%s&" % elem var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""] var method = "DELETE" var headers = {} var header = "Bearer %s" % p_session.token headers["Authorization"] = header var content : PackedByteArray var result = await _http_adapter.send_async(method, uri, headers, content) if result is NakamaException: return NakamaAsyncResult.new(result) return NakamaAsyncResult.new() # List all friends for the current user. func list_friends_async( p_session : NakamaSession , p_limit = null # : integer , p_state = null # : integer , p_cursor = null # : string ) -> ApiFriendList: var try_refresh = await _refresh_session(p_session) if try_refresh != null: if try_refresh.is_exception(): return ApiFriendList.new(try_refresh.get_exception()) await p_session.refresh(try_refresh) var urlpath : String = "/v2/friend" var query_params = "" if p_limit != null: query_params += "limit=%d&" % p_limit if p_state != null: query_params += "state=%d&" % p_state if p_cursor != null: query_params += "cursor=%s&" % NakamaSerializer.escape_http(p_cursor) var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""] var method = "GET" var headers = {} var header = "Bearer %s" % p_session.token headers["Authorization"] = header var content : PackedByteArray var result = await _http_adapter.send_async(method, uri, headers, content) if result is NakamaException: return ApiFriendList.new(result) var out : ApiFriendList = NakamaSerializer.deserialize(_namespace, "ApiFriendList", result) return out # Add friends by ID or username to a user's account. func add_friends_async( p_session : NakamaSession , p_ids = null # : array , p_usernames = null # : array ) -> NakamaAsyncResult: var try_refresh = await _refresh_session(p_session) if try_refresh != null: if try_refresh.is_exception(): return NakamaAsyncResult.new(try_refresh.get_exception()) await p_session.refresh(try_refresh) var urlpath : String = "/v2/friend" var query_params = "" if p_ids != null: for elem in p_ids: query_params += "ids=%s&" % elem if p_usernames != null: for elem in p_usernames: query_params += "usernames=%s&" % elem var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""] var method = "POST" var headers = {} var header = "Bearer %s" % p_session.token headers["Authorization"] = header var content : PackedByteArray var result = await _http_adapter.send_async(method, uri, headers, content) if result is NakamaException: return NakamaAsyncResult.new(result) return NakamaAsyncResult.new() # Block one or more users by ID or username. func block_friends_async( p_session : NakamaSession , p_ids = null # : array , p_usernames = null # : array ) -> NakamaAsyncResult: var try_refresh = await _refresh_session(p_session) if try_refresh != null: if try_refresh.is_exception(): return NakamaAsyncResult.new(try_refresh.get_exception()) await p_session.refresh(try_refresh) var urlpath : String = "/v2/friend/block" var query_params = "" if p_ids != null: for elem in p_ids: query_params += "ids=%s&" % elem if p_usernames != null: for elem in p_usernames: query_params += "usernames=%s&" % elem var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""] var method = "POST" var headers = {} var header = "Bearer %s" % p_session.token headers["Authorization"] = header var content : PackedByteArray var result = await _http_adapter.send_async(method, uri, headers, content) if result is NakamaException: return NakamaAsyncResult.new(result) return NakamaAsyncResult.new() # Import Facebook friends and add them to a user's account. func import_facebook_friends_async( p_session : NakamaSession , p_account : ApiAccountFacebook , p_reset = null # : boolean ) -> NakamaAsyncResult: var try_refresh = await _refresh_session(p_session) if try_refresh != null: if try_refresh.is_exception(): return NakamaAsyncResult.new(try_refresh.get_exception()) await p_session.refresh(try_refresh) var urlpath : String = "/v2/friend/facebook" var query_params = "" if p_reset != null: query_params += "reset=%s&" % str(bool(p_reset)).to_lower() var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""] var method = "POST" var headers = {} var header = "Bearer %s" % p_session.token headers["Authorization"] = header var content : PackedByteArray content = JSON.stringify(p_account.serialize()).to_utf8_buffer() var result = await _http_adapter.send_async(method, uri, headers, content) if result is NakamaException: return NakamaAsyncResult.new(result) return NakamaAsyncResult.new() # Import Steam friends and add them to a user's account. func import_steam_friends_async( p_session : NakamaSession , p_account : ApiAccountSteam , p_reset = null # : boolean ) -> NakamaAsyncResult: var try_refresh = await _refresh_session(p_session) if try_refresh != null: if try_refresh.is_exception(): return NakamaAsyncResult.new(try_refresh.get_exception()) await p_session.refresh(try_refresh) var urlpath : String = "/v2/friend/steam" var query_params = "" if p_reset != null: query_params += "reset=%s&" % str(bool(p_reset)).to_lower() var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""] var method = "POST" var headers = {} var header = "Bearer %s" % p_session.token headers["Authorization"] = header var content : PackedByteArray content = JSON.stringify(p_account.serialize()).to_utf8_buffer() var result = await _http_adapter.send_async(method, uri, headers, content) if result is NakamaException: return NakamaAsyncResult.new(result) return NakamaAsyncResult.new() # List groups based on given filters. func list_groups_async( p_session : NakamaSession , p_name = null # : string , p_cursor = null # : string , p_limit = null # : integer , p_lang_tag = null # : string , p_members = null # : integer , p_open = null # : boolean ) -> ApiGroupList: var try_refresh = await _refresh_session(p_session) if try_refresh != null: if try_refresh.is_exception(): return ApiGroupList.new(try_refresh.get_exception()) await p_session.refresh(try_refresh) var urlpath : String = "/v2/group" var query_params = "" if p_name != null: query_params += "name=%s&" % NakamaSerializer.escape_http(p_name) if p_cursor != null: query_params += "cursor=%s&" % NakamaSerializer.escape_http(p_cursor) if p_limit != null: query_params += "limit=%d&" % p_limit if p_lang_tag != null: query_params += "lang_tag=%s&" % NakamaSerializer.escape_http(p_lang_tag) if p_members != null: query_params += "members=%d&" % p_members if p_open != null: query_params += "open=%s&" % str(bool(p_open)).to_lower() var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""] var method = "GET" var headers = {} var header = "Bearer %s" % p_session.token headers["Authorization"] = header var content : PackedByteArray var result = await _http_adapter.send_async(method, uri, headers, content) if result is NakamaException: return ApiGroupList.new(result) var out : ApiGroupList = NakamaSerializer.deserialize(_namespace, "ApiGroupList", result) return out # Create a new group with the current user as the owner. func create_group_async( p_session : NakamaSession , p_body : ApiCreateGroupRequest ) -> ApiGroup: var try_refresh = await _refresh_session(p_session) if try_refresh != null: if try_refresh.is_exception(): return ApiGroup.new(try_refresh.get_exception()) await p_session.refresh(try_refresh) var urlpath : String = "/v2/group" var query_params = "" var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""] var method = "POST" var headers = {} var header = "Bearer %s" % p_session.token headers["Authorization"] = header var content : PackedByteArray content = JSON.stringify(p_body.serialize()).to_utf8_buffer() var result = await _http_adapter.send_async(method, uri, headers, content) if result is NakamaException: return ApiGroup.new(result) var out : ApiGroup = NakamaSerializer.deserialize(_namespace, "ApiGroup", result) return out # Delete a group by ID. func delete_group_async( p_session : NakamaSession , p_group_id : String ) -> NakamaAsyncResult: var try_refresh = await _refresh_session(p_session) if try_refresh != null: if try_refresh.is_exception(): return NakamaAsyncResult.new(try_refresh.get_exception()) await p_session.refresh(try_refresh) var urlpath : String = "/v2/group/{groupId}" urlpath = urlpath.replace("{groupId}", NakamaSerializer.escape_http(p_group_id)) var query_params = "" var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""] var method = "DELETE" var headers = {} var header = "Bearer %s" % p_session.token headers["Authorization"] = header var content : PackedByteArray var result = await _http_adapter.send_async(method, uri, headers, content) if result is NakamaException: return NakamaAsyncResult.new(result) return NakamaAsyncResult.new() # Update fields in a given group. func update_group_async( p_session : NakamaSession , p_group_id : String , p_body : ApiUpdateGroupRequest ) -> NakamaAsyncResult: var try_refresh = await _refresh_session(p_session) if try_refresh != null: if try_refresh.is_exception(): return NakamaAsyncResult.new(try_refresh.get_exception()) await p_session.refresh(try_refresh) var urlpath : String = "/v2/group/{groupId}" urlpath = urlpath.replace("{groupId}", NakamaSerializer.escape_http(p_group_id)) var query_params = "" var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""] var method = "PUT" var headers = {} var header = "Bearer %s" % p_session.token headers["Authorization"] = header var content : PackedByteArray content = JSON.stringify(p_body.serialize()).to_utf8_buffer() var result = await _http_adapter.send_async(method, uri, headers, content) if result is NakamaException: return NakamaAsyncResult.new(result) return NakamaAsyncResult.new() # Add users to a group. func add_group_users_async( p_session : NakamaSession , p_group_id : String , p_user_ids = null # : array ) -> NakamaAsyncResult: var try_refresh = await _refresh_session(p_session) if try_refresh != null: if try_refresh.is_exception(): return NakamaAsyncResult.new(try_refresh.get_exception()) await p_session.refresh(try_refresh) var urlpath : String = "/v2/group/{groupId}/add" urlpath = urlpath.replace("{groupId}", NakamaSerializer.escape_http(p_group_id)) var query_params = "" if p_user_ids != null: for elem in p_user_ids: query_params += "user_ids=%s&" % elem var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""] var method = "POST" var headers = {} var header = "Bearer %s" % p_session.token headers["Authorization"] = header var content : PackedByteArray var result = await _http_adapter.send_async(method, uri, headers, content) if result is NakamaException: return NakamaAsyncResult.new(result) return NakamaAsyncResult.new() # Ban a set of users from a group. func ban_group_users_async( p_session : NakamaSession , p_group_id : String , p_user_ids = null # : array ) -> NakamaAsyncResult: var try_refresh = await _refresh_session(p_session) if try_refresh != null: if try_refresh.is_exception(): return NakamaAsyncResult.new(try_refresh.get_exception()) await p_session.refresh(try_refresh) var urlpath : String = "/v2/group/{groupId}/ban" urlpath = urlpath.replace("{groupId}", NakamaSerializer.escape_http(p_group_id)) var query_params = "" if p_user_ids != null: for elem in p_user_ids: query_params += "user_ids=%s&" % elem var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""] var method = "POST" var headers = {} var header = "Bearer %s" % p_session.token headers["Authorization"] = header var content : PackedByteArray var result = await _http_adapter.send_async(method, uri, headers, content) if result is NakamaException: return NakamaAsyncResult.new(result) return NakamaAsyncResult.new() # Demote a set of users in a group to the next role down. func demote_group_users_async( p_session : NakamaSession , p_group_id : String , p_user_ids = null # : array ) -> NakamaAsyncResult: var try_refresh = await _refresh_session(p_session) if try_refresh != null: if try_refresh.is_exception(): return NakamaAsyncResult.new(try_refresh.get_exception()) await p_session.refresh(try_refresh) var urlpath : String = "/v2/group/{groupId}/demote" urlpath = urlpath.replace("{groupId}", NakamaSerializer.escape_http(p_group_id)) var query_params = "" if p_user_ids != null: for elem in p_user_ids: query_params += "user_ids=%s&" % elem var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""] var method = "POST" var headers = {} var header = "Bearer %s" % p_session.token headers["Authorization"] = header var content : PackedByteArray var result = await _http_adapter.send_async(method, uri, headers, content) if result is NakamaException: return NakamaAsyncResult.new(result) return NakamaAsyncResult.new() # Immediately join an open group, or request to join a closed one. func join_group_async( p_session : NakamaSession , p_group_id : String ) -> NakamaAsyncResult: var try_refresh = await _refresh_session(p_session) if try_refresh != null: if try_refresh.is_exception(): return NakamaAsyncResult.new(try_refresh.get_exception()) await p_session.refresh(try_refresh) var urlpath : String = "/v2/group/{groupId}/join" urlpath = urlpath.replace("{groupId}", NakamaSerializer.escape_http(p_group_id)) var query_params = "" var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""] var method = "POST" var headers = {} var header = "Bearer %s" % p_session.token headers["Authorization"] = header var content : PackedByteArray var result = await _http_adapter.send_async(method, uri, headers, content) if result is NakamaException: return NakamaAsyncResult.new(result) return NakamaAsyncResult.new() # Kick a set of users from a group. func kick_group_users_async( p_session : NakamaSession , p_group_id : String , p_user_ids = null # : array ) -> NakamaAsyncResult: var try_refresh = await _refresh_session(p_session) if try_refresh != null: if try_refresh.is_exception(): return NakamaAsyncResult.new(try_refresh.get_exception()) await p_session.refresh(try_refresh) var urlpath : String = "/v2/group/{groupId}/kick" urlpath = urlpath.replace("{groupId}", NakamaSerializer.escape_http(p_group_id)) var query_params = "" if p_user_ids != null: for elem in p_user_ids: query_params += "user_ids=%s&" % elem var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""] var method = "POST" var headers = {} var header = "Bearer %s" % p_session.token headers["Authorization"] = header var content : PackedByteArray var result = await _http_adapter.send_async(method, uri, headers, content) if result is NakamaException: return NakamaAsyncResult.new(result) return NakamaAsyncResult.new() # Leave a group the user is a member of. func leave_group_async( p_session : NakamaSession , p_group_id : String ) -> NakamaAsyncResult: var try_refresh = await _refresh_session(p_session) if try_refresh != null: if try_refresh.is_exception(): return NakamaAsyncResult.new(try_refresh.get_exception()) await p_session.refresh(try_refresh) var urlpath : String = "/v2/group/{groupId}/leave" urlpath = urlpath.replace("{groupId}", NakamaSerializer.escape_http(p_group_id)) var query_params = "" var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""] var method = "POST" var headers = {} var header = "Bearer %s" % p_session.token headers["Authorization"] = header var content : PackedByteArray var result = await _http_adapter.send_async(method, uri, headers, content) if result is NakamaException: return NakamaAsyncResult.new(result) return NakamaAsyncResult.new() # Promote a set of users in a group to the next role up. func promote_group_users_async( p_session : NakamaSession , p_group_id : String , p_user_ids = null # : array ) -> NakamaAsyncResult: var try_refresh = await _refresh_session(p_session) if try_refresh != null: if try_refresh.is_exception(): return NakamaAsyncResult.new(try_refresh.get_exception()) await p_session.refresh(try_refresh) var urlpath : String = "/v2/group/{groupId}/promote" urlpath = urlpath.replace("{groupId}", NakamaSerializer.escape_http(p_group_id)) var query_params = "" if p_user_ids != null: for elem in p_user_ids: query_params += "user_ids=%s&" % elem var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""] var method = "POST" var headers = {} var header = "Bearer %s" % p_session.token headers["Authorization"] = header var content : PackedByteArray var result = await _http_adapter.send_async(method, uri, headers, content) if result is NakamaException: return NakamaAsyncResult.new(result) return NakamaAsyncResult.new() # List all users that are part of a group. func list_group_users_async( p_session : NakamaSession , p_group_id : String , p_limit = null # : integer , p_state = null # : integer , p_cursor = null # : string ) -> ApiGroupUserList: var try_refresh = await _refresh_session(p_session) if try_refresh != null: if try_refresh.is_exception(): return ApiGroupUserList.new(try_refresh.get_exception()) await p_session.refresh(try_refresh) var urlpath : String = "/v2/group/{groupId}/user" urlpath = urlpath.replace("{groupId}", NakamaSerializer.escape_http(p_group_id)) var query_params = "" if p_limit != null: query_params += "limit=%d&" % p_limit if p_state != null: query_params += "state=%d&" % p_state if p_cursor != null: query_params += "cursor=%s&" % NakamaSerializer.escape_http(p_cursor) var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""] var method = "GET" var headers = {} var header = "Bearer %s" % p_session.token headers["Authorization"] = header var content : PackedByteArray var result = await _http_adapter.send_async(method, uri, headers, content) if result is NakamaException: return ApiGroupUserList.new(result) var out : ApiGroupUserList = NakamaSerializer.deserialize(_namespace, "ApiGroupUserList", result) return out # Validate Apple IAP Receipt func validate_purchase_apple_async( p_session : NakamaSession , p_body : ApiValidatePurchaseAppleRequest ) -> ApiValidatePurchaseResponse: var try_refresh = await _refresh_session(p_session) if try_refresh != null: if try_refresh.is_exception(): return ApiValidatePurchaseResponse.new(try_refresh.get_exception()) await p_session.refresh(try_refresh) var urlpath : String = "/v2/iap/purchase/apple" var query_params = "" var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""] var method = "POST" var headers = {} var header = "Bearer %s" % p_session.token headers["Authorization"] = header var content : PackedByteArray content = JSON.stringify(p_body.serialize()).to_utf8_buffer() var result = await _http_adapter.send_async(method, uri, headers, content) if result is NakamaException: return ApiValidatePurchaseResponse.new(result) var out : ApiValidatePurchaseResponse = NakamaSerializer.deserialize(_namespace, "ApiValidatePurchaseResponse", result) return out # Validate Google IAP Receipt func validate_purchase_google_async( p_session : NakamaSession , p_body : ApiValidatePurchaseGoogleRequest ) -> ApiValidatePurchaseResponse: var try_refresh = await _refresh_session(p_session) if try_refresh != null: if try_refresh.is_exception(): return ApiValidatePurchaseResponse.new(try_refresh.get_exception()) await p_session.refresh(try_refresh) var urlpath : String = "/v2/iap/purchase/google" var query_params = "" var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""] var method = "POST" var headers = {} var header = "Bearer %s" % p_session.token headers["Authorization"] = header var content : PackedByteArray content = JSON.stringify(p_body.serialize()).to_utf8_buffer() var result = await _http_adapter.send_async(method, uri, headers, content) if result is NakamaException: return ApiValidatePurchaseResponse.new(result) var out : ApiValidatePurchaseResponse = NakamaSerializer.deserialize(_namespace, "ApiValidatePurchaseResponse", result) return out # Validate Huawei IAP Receipt func validate_purchase_huawei_async( p_session : NakamaSession , p_body : ApiValidatePurchaseHuaweiRequest ) -> ApiValidatePurchaseResponse: var try_refresh = await _refresh_session(p_session) if try_refresh != null: if try_refresh.is_exception(): return ApiValidatePurchaseResponse.new(try_refresh.get_exception()) await p_session.refresh(try_refresh) var urlpath : String = "/v2/iap/purchase/huawei" var query_params = "" var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""] var method = "POST" var headers = {} var header = "Bearer %s" % p_session.token headers["Authorization"] = header var content : PackedByteArray content = JSON.stringify(p_body.serialize()).to_utf8_buffer() var result = await _http_adapter.send_async(method, uri, headers, content) if result is NakamaException: return ApiValidatePurchaseResponse.new(result) var out : ApiValidatePurchaseResponse = NakamaSerializer.deserialize(_namespace, "ApiValidatePurchaseResponse", result) return out # List user's subscriptions. func list_subscriptions_async( p_session : NakamaSession , p_body : ApiListSubscriptionsRequest ) -> ApiSubscriptionList: var try_refresh = await _refresh_session(p_session) if try_refresh != null: if try_refresh.is_exception(): return ApiSubscriptionList.new(try_refresh.get_exception()) await p_session.refresh(try_refresh) var urlpath : String = "/v2/iap/subscription" var query_params = "" var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""] var method = "POST" var headers = {} var header = "Bearer %s" % p_session.token headers["Authorization"] = header var content : PackedByteArray content = JSON.stringify(p_body.serialize()).to_utf8_buffer() var result = await _http_adapter.send_async(method, uri, headers, content) if result is NakamaException: return ApiSubscriptionList.new(result) var out : ApiSubscriptionList = NakamaSerializer.deserialize(_namespace, "ApiSubscriptionList", result) return out # Validate Apple Subscription Receipt func validate_subscription_apple_async( p_session : NakamaSession , p_body : ApiValidateSubscriptionAppleRequest ) -> ApiValidateSubscriptionResponse: var try_refresh = await _refresh_session(p_session) if try_refresh != null: if try_refresh.is_exception(): return ApiValidateSubscriptionResponse.new(try_refresh.get_exception()) await p_session.refresh(try_refresh) var urlpath : String = "/v2/iap/subscription/apple" var query_params = "" var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""] var method = "POST" var headers = {} var header = "Bearer %s" % p_session.token headers["Authorization"] = header var content : PackedByteArray content = JSON.stringify(p_body.serialize()).to_utf8_buffer() var result = await _http_adapter.send_async(method, uri, headers, content) if result is NakamaException: return ApiValidateSubscriptionResponse.new(result) var out : ApiValidateSubscriptionResponse = NakamaSerializer.deserialize(_namespace, "ApiValidateSubscriptionResponse", result) return out # Validate Google Subscription Receipt func validate_subscription_google_async( p_session : NakamaSession , p_body : ApiValidateSubscriptionGoogleRequest ) -> ApiValidateSubscriptionResponse: var try_refresh = await _refresh_session(p_session) if try_refresh != null: if try_refresh.is_exception(): return ApiValidateSubscriptionResponse.new(try_refresh.get_exception()) await p_session.refresh(try_refresh) var urlpath : String = "/v2/iap/subscription/google" var query_params = "" var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""] var method = "POST" var headers = {} var header = "Bearer %s" % p_session.token headers["Authorization"] = header var content : PackedByteArray content = JSON.stringify(p_body.serialize()).to_utf8_buffer() var result = await _http_adapter.send_async(method, uri, headers, content) if result is NakamaException: return ApiValidateSubscriptionResponse.new(result) var out : ApiValidateSubscriptionResponse = NakamaSerializer.deserialize(_namespace, "ApiValidateSubscriptionResponse", result) return out # Get subscription by product id. func get_subscription_async( p_session : NakamaSession , p_product_id : String ) -> ApiValidatedSubscription: var try_refresh = await _refresh_session(p_session) if try_refresh != null: if try_refresh.is_exception(): return ApiValidatedSubscription.new(try_refresh.get_exception()) await p_session.refresh(try_refresh) var urlpath : String = "/v2/iap/subscription/{productId}" urlpath = urlpath.replace("{productId}", NakamaSerializer.escape_http(p_product_id)) var query_params = "" var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""] var method = "GET" var headers = {} var header = "Bearer %s" % p_session.token headers["Authorization"] = header var content : PackedByteArray var result = await _http_adapter.send_async(method, uri, headers, content) if result is NakamaException: return ApiValidatedSubscription.new(result) var out : ApiValidatedSubscription = NakamaSerializer.deserialize(_namespace, "ApiValidatedSubscription", result) return out # Delete a leaderboard record. func delete_leaderboard_record_async( p_session : NakamaSession , p_leaderboard_id : String ) -> NakamaAsyncResult: var try_refresh = await _refresh_session(p_session) if try_refresh != null: if try_refresh.is_exception(): return NakamaAsyncResult.new(try_refresh.get_exception()) await p_session.refresh(try_refresh) var urlpath : String = "/v2/leaderboard/{leaderboardId}" urlpath = urlpath.replace("{leaderboardId}", NakamaSerializer.escape_http(p_leaderboard_id)) var query_params = "" var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""] var method = "DELETE" var headers = {} var header = "Bearer %s" % p_session.token headers["Authorization"] = header var content : PackedByteArray var result = await _http_adapter.send_async(method, uri, headers, content) if result is NakamaException: return NakamaAsyncResult.new(result) return NakamaAsyncResult.new() # List leaderboard records. func list_leaderboard_records_async( p_session : NakamaSession , p_leaderboard_id : String , p_owner_ids = null # : array , p_limit = null # : integer , p_cursor = null # : string , p_expiry = null # : string ) -> ApiLeaderboardRecordList: var try_refresh = await _refresh_session(p_session) if try_refresh != null: if try_refresh.is_exception(): return ApiLeaderboardRecordList.new(try_refresh.get_exception()) await p_session.refresh(try_refresh) var urlpath : String = "/v2/leaderboard/{leaderboardId}" urlpath = urlpath.replace("{leaderboardId}", NakamaSerializer.escape_http(p_leaderboard_id)) var query_params = "" if p_owner_ids != null: for elem in p_owner_ids: query_params += "owner_ids=%s&" % elem if p_limit != null: query_params += "limit=%d&" % p_limit if p_cursor != null: query_params += "cursor=%s&" % NakamaSerializer.escape_http(p_cursor) if p_expiry != null: query_params += "expiry=%s&" % NakamaSerializer.escape_http(p_expiry) var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""] var method = "GET" var headers = {} var header = "Bearer %s" % p_session.token headers["Authorization"] = header var content : PackedByteArray var result = await _http_adapter.send_async(method, uri, headers, content) if result is NakamaException: return ApiLeaderboardRecordList.new(result) var out : ApiLeaderboardRecordList = NakamaSerializer.deserialize(_namespace, "ApiLeaderboardRecordList", result) return out # Write a record to a leaderboard. func write_leaderboard_record_async( p_session : NakamaSession , p_leaderboard_id : String , p_record : WriteLeaderboardRecordRequestLeaderboardRecordWrite ) -> ApiLeaderboardRecord: var try_refresh = await _refresh_session(p_session) if try_refresh != null: if try_refresh.is_exception(): return ApiLeaderboardRecord.new(try_refresh.get_exception()) await p_session.refresh(try_refresh) var urlpath : String = "/v2/leaderboard/{leaderboardId}" urlpath = urlpath.replace("{leaderboardId}", NakamaSerializer.escape_http(p_leaderboard_id)) var query_params = "" var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""] var method = "POST" var headers = {} var header = "Bearer %s" % p_session.token headers["Authorization"] = header var content : PackedByteArray content = JSON.stringify(p_record.serialize()).to_utf8_buffer() var result = await _http_adapter.send_async(method, uri, headers, content) if result is NakamaException: return ApiLeaderboardRecord.new(result) var out : ApiLeaderboardRecord = NakamaSerializer.deserialize(_namespace, "ApiLeaderboardRecord", result) return out # List leaderboard records that belong to a user. func list_leaderboard_records_around_owner_async( p_session : NakamaSession , p_leaderboard_id : String , p_owner_id : String , p_limit = null # : integer , p_expiry = null # : string , p_cursor = null # : string ) -> ApiLeaderboardRecordList: var try_refresh = await _refresh_session(p_session) if try_refresh != null: if try_refresh.is_exception(): return ApiLeaderboardRecordList.new(try_refresh.get_exception()) await p_session.refresh(try_refresh) var urlpath : String = "/v2/leaderboard/{leaderboardId}/owner/{ownerId}" urlpath = urlpath.replace("{leaderboardId}", NakamaSerializer.escape_http(p_leaderboard_id)) urlpath = urlpath.replace("{ownerId}", NakamaSerializer.escape_http(p_owner_id)) var query_params = "" if p_limit != null: query_params += "limit=%d&" % p_limit if p_expiry != null: query_params += "expiry=%s&" % NakamaSerializer.escape_http(p_expiry) if p_cursor != null: query_params += "cursor=%s&" % NakamaSerializer.escape_http(p_cursor) var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""] var method = "GET" var headers = {} var header = "Bearer %s" % p_session.token headers["Authorization"] = header var content : PackedByteArray var result = await _http_adapter.send_async(method, uri, headers, content) if result is NakamaException: return ApiLeaderboardRecordList.new(result) var out : ApiLeaderboardRecordList = NakamaSerializer.deserialize(_namespace, "ApiLeaderboardRecordList", result) return out # Fetch list of running matches. func list_matches_async( p_session : NakamaSession , p_limit = null # : integer , p_authoritative = null # : boolean , p_label = null # : string , p_min_size = null # : integer , p_max_size = null # : integer , p_query = null # : string ) -> ApiMatchList: var try_refresh = await _refresh_session(p_session) if try_refresh != null: if try_refresh.is_exception(): return ApiMatchList.new(try_refresh.get_exception()) await p_session.refresh(try_refresh) var urlpath : String = "/v2/match" var query_params = "" if p_limit != null: query_params += "limit=%d&" % p_limit if p_authoritative != null: query_params += "authoritative=%s&" % str(bool(p_authoritative)).to_lower() if p_label != null: query_params += "label=%s&" % NakamaSerializer.escape_http(p_label) if p_min_size != null: query_params += "min_size=%d&" % p_min_size if p_max_size != null: query_params += "max_size=%d&" % p_max_size if p_query != null: query_params += "query=%s&" % NakamaSerializer.escape_http(p_query) var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""] var method = "GET" var headers = {} var header = "Bearer %s" % p_session.token headers["Authorization"] = header var content : PackedByteArray var result = await _http_adapter.send_async(method, uri, headers, content) if result is NakamaException: return ApiMatchList.new(result) var out : ApiMatchList = NakamaSerializer.deserialize(_namespace, "ApiMatchList", result) return out # Delete one or more notifications for the current user. func delete_notifications_async( p_session : NakamaSession , p_ids = null # : array ) -> NakamaAsyncResult: var try_refresh = await _refresh_session(p_session) if try_refresh != null: if try_refresh.is_exception(): return NakamaAsyncResult.new(try_refresh.get_exception()) await p_session.refresh(try_refresh) var urlpath : String = "/v2/notification" var query_params = "" if p_ids != null: for elem in p_ids: query_params += "ids=%s&" % elem var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""] var method = "DELETE" var headers = {} var header = "Bearer %s" % p_session.token headers["Authorization"] = header var content : PackedByteArray var result = await _http_adapter.send_async(method, uri, headers, content) if result is NakamaException: return NakamaAsyncResult.new(result) return NakamaAsyncResult.new() # Fetch list of notifications. func list_notifications_async( p_session : NakamaSession , p_limit = null # : integer , p_cacheable_cursor = null # : string ) -> ApiNotificationList: var try_refresh = await _refresh_session(p_session) if try_refresh != null: if try_refresh.is_exception(): return ApiNotificationList.new(try_refresh.get_exception()) await p_session.refresh(try_refresh) var urlpath : String = "/v2/notification" var query_params = "" if p_limit != null: query_params += "limit=%d&" % p_limit if p_cacheable_cursor != null: query_params += "cacheable_cursor=%s&" % NakamaSerializer.escape_http(p_cacheable_cursor) var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""] var method = "GET" var headers = {} var header = "Bearer %s" % p_session.token headers["Authorization"] = header var content : PackedByteArray var result = await _http_adapter.send_async(method, uri, headers, content) if result is NakamaException: return ApiNotificationList.new(result) var out : ApiNotificationList = NakamaSerializer.deserialize(_namespace, "ApiNotificationList", result) return out # Execute a Lua function on the server. func rpc_func2_async( p_bearer_token : String , p_id : String , p_payload = null # : string , p_http_key = null # : string ) -> ApiRpc: var urlpath : String = "/v2/rpc/{id}" urlpath = urlpath.replace("{id}", NakamaSerializer.escape_http(p_id)) var query_params = "" if p_payload != null: query_params += "payload=%s&" % NakamaSerializer.escape_http(p_payload) if p_http_key != null: query_params += "http_key=%s&" % NakamaSerializer.escape_http(p_http_key) var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""] var method = "GET" var headers = {} if (p_bearer_token): var header = "Bearer %s" % p_bearer_token headers["Authorization"] = header var content : PackedByteArray var result = await _http_adapter.send_async(method, uri, headers, content) if result is NakamaException: return ApiRpc.new(result) var out : ApiRpc = NakamaSerializer.deserialize(_namespace, "ApiRpc", result) return out # Execute a Lua function on the server. func rpc_func_async( p_bearer_token : String , p_id : String , p_payload : String , p_http_key = null # : string ) -> ApiRpc: var urlpath : String = "/v2/rpc/{id}" urlpath = urlpath.replace("{id}", NakamaSerializer.escape_http(p_id)) var query_params = "" if p_http_key != null: query_params += "http_key=%s&" % NakamaSerializer.escape_http(p_http_key) var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""] var method = "POST" var headers = {} if (p_bearer_token): var header = "Bearer %s" % p_bearer_token headers["Authorization"] = header var content : PackedByteArray content = JSON.stringify(p_payload).to_utf8_buffer() var result = await _http_adapter.send_async(method, uri, headers, content) if result is NakamaException: return ApiRpc.new(result) var out : ApiRpc = NakamaSerializer.deserialize(_namespace, "ApiRpc", result) return out # Log out a session, invalidate a refresh token, or log out all sessions/refresh tokens for a user. func session_logout_async( p_session : NakamaSession , p_body : ApiSessionLogoutRequest ) -> NakamaAsyncResult: var try_refresh = await _refresh_session(p_session) if try_refresh != null: if try_refresh.is_exception(): return NakamaAsyncResult.new(try_refresh.get_exception()) await p_session.refresh(try_refresh) var urlpath : String = "/v2/session/logout" var query_params = "" var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""] var method = "POST" var headers = {} var header = "Bearer %s" % p_session.token headers["Authorization"] = header var content : PackedByteArray content = JSON.stringify(p_body.serialize()).to_utf8_buffer() var result = await _http_adapter.send_async(method, uri, headers, content) if result is NakamaException: return NakamaAsyncResult.new(result) return NakamaAsyncResult.new() # Get storage objects. func read_storage_objects_async( p_session : NakamaSession , p_body : ApiReadStorageObjectsRequest ) -> ApiStorageObjects: var try_refresh = await _refresh_session(p_session) if try_refresh != null: if try_refresh.is_exception(): return ApiStorageObjects.new(try_refresh.get_exception()) await p_session.refresh(try_refresh) var urlpath : String = "/v2/storage" var query_params = "" var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""] var method = "POST" var headers = {} var header = "Bearer %s" % p_session.token headers["Authorization"] = header var content : PackedByteArray content = JSON.stringify(p_body.serialize()).to_utf8_buffer() var result = await _http_adapter.send_async(method, uri, headers, content) if result is NakamaException: return ApiStorageObjects.new(result) var out : ApiStorageObjects = NakamaSerializer.deserialize(_namespace, "ApiStorageObjects", result) return out # Write objects into the storage engine. func write_storage_objects_async( p_session : NakamaSession , p_body : ApiWriteStorageObjectsRequest ) -> ApiStorageObjectAcks: var try_refresh = await _refresh_session(p_session) if try_refresh != null: if try_refresh.is_exception(): return ApiStorageObjectAcks.new(try_refresh.get_exception()) await p_session.refresh(try_refresh) var urlpath : String = "/v2/storage" var query_params = "" var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""] var method = "PUT" var headers = {} var header = "Bearer %s" % p_session.token headers["Authorization"] = header var content : PackedByteArray content = JSON.stringify(p_body.serialize()).to_utf8_buffer() var result = await _http_adapter.send_async(method, uri, headers, content) if result is NakamaException: return ApiStorageObjectAcks.new(result) var out : ApiStorageObjectAcks = NakamaSerializer.deserialize(_namespace, "ApiStorageObjectAcks", result) return out # Delete one or more objects by ID or username. func delete_storage_objects_async( p_session : NakamaSession , p_body : ApiDeleteStorageObjectsRequest ) -> NakamaAsyncResult: var try_refresh = await _refresh_session(p_session) if try_refresh != null: if try_refresh.is_exception(): return NakamaAsyncResult.new(try_refresh.get_exception()) await p_session.refresh(try_refresh) var urlpath : String = "/v2/storage/delete" var query_params = "" var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""] var method = "PUT" var headers = {} var header = "Bearer %s" % p_session.token headers["Authorization"] = header var content : PackedByteArray content = JSON.stringify(p_body.serialize()).to_utf8_buffer() var result = await _http_adapter.send_async(method, uri, headers, content) if result is NakamaException: return NakamaAsyncResult.new(result) return NakamaAsyncResult.new() # List publicly readable storage objects in a given collection. func list_storage_objects_async( p_session : NakamaSession , p_collection : String , p_user_id = null # : string , p_limit = null # : integer , p_cursor = null # : string ) -> ApiStorageObjectList: var try_refresh = await _refresh_session(p_session) if try_refresh != null: if try_refresh.is_exception(): return ApiStorageObjectList.new(try_refresh.get_exception()) await p_session.refresh(try_refresh) var urlpath : String = "/v2/storage/{collection}" urlpath = urlpath.replace("{collection}", NakamaSerializer.escape_http(p_collection)) var query_params = "" if p_user_id != null: query_params += "user_id=%s&" % NakamaSerializer.escape_http(p_user_id) if p_limit != null: query_params += "limit=%d&" % p_limit if p_cursor != null: query_params += "cursor=%s&" % NakamaSerializer.escape_http(p_cursor) var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""] var method = "GET" var headers = {} var header = "Bearer %s" % p_session.token headers["Authorization"] = header var content : PackedByteArray var result = await _http_adapter.send_async(method, uri, headers, content) if result is NakamaException: return ApiStorageObjectList.new(result) var out : ApiStorageObjectList = NakamaSerializer.deserialize(_namespace, "ApiStorageObjectList", result) return out # List publicly readable storage objects in a given collection. func list_storage_objects2_async( p_session : NakamaSession , p_collection : String , p_user_id : String , p_limit = null # : integer , p_cursor = null # : string ) -> ApiStorageObjectList: var try_refresh = await _refresh_session(p_session) if try_refresh != null: if try_refresh.is_exception(): return ApiStorageObjectList.new(try_refresh.get_exception()) await p_session.refresh(try_refresh) var urlpath : String = "/v2/storage/{collection}/{userId}" urlpath = urlpath.replace("{collection}", NakamaSerializer.escape_http(p_collection)) urlpath = urlpath.replace("{userId}", NakamaSerializer.escape_http(p_user_id)) var query_params = "" if p_limit != null: query_params += "limit=%d&" % p_limit if p_cursor != null: query_params += "cursor=%s&" % NakamaSerializer.escape_http(p_cursor) var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""] var method = "GET" var headers = {} var header = "Bearer %s" % p_session.token headers["Authorization"] = header var content : PackedByteArray var result = await _http_adapter.send_async(method, uri, headers, content) if result is NakamaException: return ApiStorageObjectList.new(result) var out : ApiStorageObjectList = NakamaSerializer.deserialize(_namespace, "ApiStorageObjectList", result) return out # List current or upcoming tournaments. func list_tournaments_async( p_session : NakamaSession , p_category_start = null # : integer , p_category_end = null # : integer , p_start_time = null # : integer , p_end_time = null # : integer , p_limit = null # : integer , p_cursor = null # : string ) -> ApiTournamentList: var try_refresh = await _refresh_session(p_session) if try_refresh != null: if try_refresh.is_exception(): return ApiTournamentList.new(try_refresh.get_exception()) await p_session.refresh(try_refresh) var urlpath : String = "/v2/tournament" var query_params = "" if p_category_start != null: query_params += "category_start=%d&" % p_category_start if p_category_end != null: query_params += "category_end=%d&" % p_category_end if p_start_time != null: query_params += "start_time=%d&" % p_start_time if p_end_time != null: query_params += "end_time=%d&" % p_end_time if p_limit != null: query_params += "limit=%d&" % p_limit if p_cursor != null: query_params += "cursor=%s&" % NakamaSerializer.escape_http(p_cursor) var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""] var method = "GET" var headers = {} var header = "Bearer %s" % p_session.token headers["Authorization"] = header var content : PackedByteArray var result = await _http_adapter.send_async(method, uri, headers, content) if result is NakamaException: return ApiTournamentList.new(result) var out : ApiTournamentList = NakamaSerializer.deserialize(_namespace, "ApiTournamentList", result) return out # List tournament records. func list_tournament_records_async( p_session : NakamaSession , p_tournament_id : String , p_owner_ids = null # : array , p_limit = null # : integer , p_cursor = null # : string , p_expiry = null # : string ) -> ApiTournamentRecordList: var try_refresh = await _refresh_session(p_session) if try_refresh != null: if try_refresh.is_exception(): return ApiTournamentRecordList.new(try_refresh.get_exception()) await p_session.refresh(try_refresh) var urlpath : String = "/v2/tournament/{tournamentId}" urlpath = urlpath.replace("{tournamentId}", NakamaSerializer.escape_http(p_tournament_id)) var query_params = "" if p_owner_ids != null: for elem in p_owner_ids: query_params += "owner_ids=%s&" % elem if p_limit != null: query_params += "limit=%d&" % p_limit if p_cursor != null: query_params += "cursor=%s&" % NakamaSerializer.escape_http(p_cursor) if p_expiry != null: query_params += "expiry=%s&" % NakamaSerializer.escape_http(p_expiry) var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""] var method = "GET" var headers = {} var header = "Bearer %s" % p_session.token headers["Authorization"] = header var content : PackedByteArray var result = await _http_adapter.send_async(method, uri, headers, content) if result is NakamaException: return ApiTournamentRecordList.new(result) var out : ApiTournamentRecordList = NakamaSerializer.deserialize(_namespace, "ApiTournamentRecordList", result) return out # Write a record to a tournament. func write_tournament_record2_async( p_session : NakamaSession , p_tournament_id : String , p_record : WriteTournamentRecordRequestTournamentRecordWrite ) -> ApiLeaderboardRecord: var try_refresh = await _refresh_session(p_session) if try_refresh != null: if try_refresh.is_exception(): return ApiLeaderboardRecord.new(try_refresh.get_exception()) await p_session.refresh(try_refresh) var urlpath : String = "/v2/tournament/{tournamentId}" urlpath = urlpath.replace("{tournamentId}", NakamaSerializer.escape_http(p_tournament_id)) var query_params = "" var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""] var method = "POST" var headers = {} var header = "Bearer %s" % p_session.token headers["Authorization"] = header var content : PackedByteArray content = JSON.stringify(p_record.serialize()).to_utf8_buffer() var result = await _http_adapter.send_async(method, uri, headers, content) if result is NakamaException: return ApiLeaderboardRecord.new(result) var out : ApiLeaderboardRecord = NakamaSerializer.deserialize(_namespace, "ApiLeaderboardRecord", result) return out # Write a record to a tournament. func write_tournament_record_async( p_session : NakamaSession , p_tournament_id : String , p_record : WriteTournamentRecordRequestTournamentRecordWrite ) -> ApiLeaderboardRecord: var try_refresh = await _refresh_session(p_session) if try_refresh != null: if try_refresh.is_exception(): return ApiLeaderboardRecord.new(try_refresh.get_exception()) await p_session.refresh(try_refresh) var urlpath : String = "/v2/tournament/{tournamentId}" urlpath = urlpath.replace("{tournamentId}", NakamaSerializer.escape_http(p_tournament_id)) var query_params = "" var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""] var method = "PUT" var headers = {} var header = "Bearer %s" % p_session.token headers["Authorization"] = header var content : PackedByteArray content = JSON.stringify(p_record.serialize()).to_utf8_buffer() var result = await _http_adapter.send_async(method, uri, headers, content) if result is NakamaException: return ApiLeaderboardRecord.new(result) var out : ApiLeaderboardRecord = NakamaSerializer.deserialize(_namespace, "ApiLeaderboardRecord", result) return out # Attempt to join an open and running tournament. func join_tournament_async( p_session : NakamaSession , p_tournament_id : String ) -> NakamaAsyncResult: var try_refresh = await _refresh_session(p_session) if try_refresh != null: if try_refresh.is_exception(): return NakamaAsyncResult.new(try_refresh.get_exception()) await p_session.refresh(try_refresh) var urlpath : String = "/v2/tournament/{tournamentId}/join" urlpath = urlpath.replace("{tournamentId}", NakamaSerializer.escape_http(p_tournament_id)) var query_params = "" var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""] var method = "POST" var headers = {} var header = "Bearer %s" % p_session.token headers["Authorization"] = header var content : PackedByteArray var result = await _http_adapter.send_async(method, uri, headers, content) if result is NakamaException: return NakamaAsyncResult.new(result) return NakamaAsyncResult.new() # List tournament records for a given owner. func list_tournament_records_around_owner_async( p_session : NakamaSession , p_tournament_id : String , p_owner_id : String , p_limit = null # : integer , p_expiry = null # : string , p_cursor = null # : string ) -> ApiTournamentRecordList: var try_refresh = await _refresh_session(p_session) if try_refresh != null: if try_refresh.is_exception(): return ApiTournamentRecordList.new(try_refresh.get_exception()) await p_session.refresh(try_refresh) var urlpath : String = "/v2/tournament/{tournamentId}/owner/{ownerId}" urlpath = urlpath.replace("{tournamentId}", NakamaSerializer.escape_http(p_tournament_id)) urlpath = urlpath.replace("{ownerId}", NakamaSerializer.escape_http(p_owner_id)) var query_params = "" if p_limit != null: query_params += "limit=%d&" % p_limit if p_expiry != null: query_params += "expiry=%s&" % NakamaSerializer.escape_http(p_expiry) if p_cursor != null: query_params += "cursor=%s&" % NakamaSerializer.escape_http(p_cursor) var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""] var method = "GET" var headers = {} var header = "Bearer %s" % p_session.token headers["Authorization"] = header var content : PackedByteArray var result = await _http_adapter.send_async(method, uri, headers, content) if result is NakamaException: return ApiTournamentRecordList.new(result) var out : ApiTournamentRecordList = NakamaSerializer.deserialize(_namespace, "ApiTournamentRecordList", result) return out # Fetch zero or more users by ID and/or username. func get_users_async( p_session : NakamaSession , p_ids = null # : array , p_usernames = null # : array , p_facebook_ids = null # : array ) -> ApiUsers: var try_refresh = await _refresh_session(p_session) if try_refresh != null: if try_refresh.is_exception(): return ApiUsers.new(try_refresh.get_exception()) await p_session.refresh(try_refresh) var urlpath : String = "/v2/user" var query_params = "" if p_ids != null: for elem in p_ids: query_params += "ids=%s&" % elem if p_usernames != null: for elem in p_usernames: query_params += "usernames=%s&" % elem if p_facebook_ids != null: for elem in p_facebook_ids: query_params += "facebook_ids=%s&" % elem var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""] var method = "GET" var headers = {} var header = "Bearer %s" % p_session.token headers["Authorization"] = header var content : PackedByteArray var result = await _http_adapter.send_async(method, uri, headers, content) if result is NakamaException: return ApiUsers.new(result) var out : ApiUsers = NakamaSerializer.deserialize(_namespace, "ApiUsers", result) return out # List groups the current user belongs to. func list_user_groups_async( p_session : NakamaSession , p_user_id : String , p_limit = null # : integer , p_state = null # : integer , p_cursor = null # : string ) -> ApiUserGroupList: var try_refresh = await _refresh_session(p_session) if try_refresh != null: if try_refresh.is_exception(): return ApiUserGroupList.new(try_refresh.get_exception()) await p_session.refresh(try_refresh) var urlpath : String = "/v2/user/{userId}/group" urlpath = urlpath.replace("{userId}", NakamaSerializer.escape_http(p_user_id)) var query_params = "" if p_limit != null: query_params += "limit=%d&" % p_limit if p_state != null: query_params += "state=%d&" % p_state if p_cursor != null: query_params += "cursor=%s&" % NakamaSerializer.escape_http(p_cursor) var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""] var method = "GET" var headers = {} var header = "Bearer %s" % p_session.token headers["Authorization"] = header var content : PackedByteArray var result = await _http_adapter.send_async(method, uri, headers, content) if result is NakamaException: return ApiUserGroupList.new(result) var out : ApiUserGroupList = NakamaSerializer.deserialize(_namespace, "ApiUserGroupList", result) return out ================================================ FILE: addons/com.heroiclabs.nakama/api/NakamaRTAPI.gd ================================================ extends NakamaAsyncResult class_name NakamaRTAPI # A chat channel on the server. class Channel extends NakamaAsyncResult: const _SCHEMA = { "id": {"name": "id", "type": TYPE_STRING, "required": true}, "presences": {"name": "presences", "type": TYPE_ARRAY, "required": false, "content": "UserPresence"}, "self": {"name": "self_presence", "type": "UserPresence", "required": true}, "room_name": {"name": "room_name", "type": TYPE_STRING, "required": false}, "group_id": {"name": "group_id", "type": TYPE_STRING, "required": false}, "user_id_one": {"name": "user_id_one", "type": TYPE_STRING, "required": false}, "user_id_two": {"name": "user_id_two", "type": TYPE_STRING, "required": false} } # The server-assigned channel ID. var id : String # The presences visible on the chat channel. var presences : Array # of objects NakamaUserPresence # The presence of the current user. i.e. Your self. var self_presence : NakamaRTAPI.UserPresence # The name of the chat room, or an empty string if this message was not sent through a chat room. var room_name : String # The ID of the group, or an empty string if this message was not sent through a group channel. var group_id : String # The ID of the first DM user, or an empty string if this message was not sent through a DM chat. var user_id_one : String # The ID of the second DM user, or an empty string if this message was not sent through a DM chat. var user_id_two : String func _init(p_ex = null): super(p_ex) func _to_string(): if is_exception(): return get_exception()._to_string() return "Channel" % [ id, presences, self_presence, room_name, group_id, user_id_one, user_id_two ] static func create(p_ns : GDScript, p_dict : Dictionary) -> Channel: return _safe_ret(NakamaSerializer.deserialize(p_ns, "Channel", p_dict), Channel) as Channel static func get_result_key() -> String: return "channel" class ChannelMessageAck extends NakamaAsyncResult: const _SCHEMA = { "channel_id": {"name": "channel_id", "type": TYPE_STRING, "required": true}, "code": {"name": "code", "type": TYPE_INT, "required": true}, "create_time": {"name": "create_time", "type": TYPE_STRING, "required": false}, "message_id": {"name": "message_id", "type": TYPE_STRING, "required": true}, "persistent": {"name": "persistent", "type": TYPE_BOOL, "required": false}, "update_time": {"name": "update_time", "type": TYPE_STRING, "required": false}, "username": {"name": "username", "type": TYPE_STRING, "required": false}, "room_name": {"name": "room_name", "type": TYPE_STRING, "required": false}, "group_id": {"name": "group_id", "type": TYPE_STRING, "required": false}, "user_id_one": {"name": "user_id_one", "type": TYPE_STRING, "required": false}, "user_id_two": {"name": "user_id_two", "type": TYPE_STRING, "required": false} } # The server-assigned channel ID. var channel_id : String # A user-defined code for the chat message. var code : int # The UNIX time when the message was created. var create_time : String # A unique ID for the chat message. var message_id : String # True if the chat message has been stored in history. var persistent : bool # The UNIX time when the message was updated. var update_time : String # The username of the sender of the message. var username : String # The name of the chat room, or an empty string if this message was not sent through a chat room. var room_name : String # The ID of the group, or an empty string if this message was not sent through a group channel. var group_id : String # The ID of the first DM user, or an empty string if this message was not sent through a DM chat. var user_id_one : String # The ID of the second DM user, or an empty string if this message was not sent through a DM chat. var user_id_two : String func _init(p_ex = null): super(p_ex) func _to_string(): if is_exception(): return get_exception()._to_string() return "ChannelMessageAck" % [ channel_id, code, create_time, message_id, persistent, update_time, username, room_name, group_id, user_id_one, user_id_two ] static func create(p_ns : GDScript, p_dict : Dictionary) -> ChannelMessageAck: return _safe_ret(NakamaSerializer.deserialize(p_ns, "ChannelMessageAck", p_dict), ChannelMessageAck) as ChannelMessageAck static func get_result_key() -> String: return "channel_message_ack" # A batch of join and leave presences on a chat channel. class ChannelPresenceEvent extends NakamaAsyncResult: const _SCHEMA = { "channel_id": {"name": "channel_id", "type": TYPE_STRING, "required": true}, "joins": {"name": "joins", "type": TYPE_ARRAY, "required": false, "content" : "UserPresence"}, "leaves": {"name": "leaves", "type": TYPE_ARRAY, "required": false, "content" : "UserPresence"}, "room_name": {"name": "room_name", "type": TYPE_STRING, "required": false}, "group_id": {"name": "group_id", "type": TYPE_STRING, "required": false}, "user_id_one": {"name": "user_id_one", "type": TYPE_STRING, "required": false}, "user_id_two": {"name": "user_id_two", "type": TYPE_STRING, "required": false} } # The unique identifier of the chat channel. var channel_id : String # Presences of the users who joined the channel. var joins : Array # UserPresence # Presences of users who left the channel. var leaves : Array # UserPresence # The name of the chat room, or an empty string if this message was not sent through a chat room. var room_name : String # The ID of the group, or an empty string if this message was not sent through a group channel. var group_id : String # The ID of the first DM user, or an empty string if this message was not sent through a DM chat. var user_id_one : String # The ID of the second DM user, or an empty string if this message was not sent through a DM chat. var user_id_two : String func _init(p_ex = null): super(p_ex) func _to_string(): if is_exception(): return get_exception()._to_string() return "ChannelPresenceEvent" % [ channel_id, joins, leaves, room_name, group_id, user_id_one, user_id_two ] static func create(p_ns : GDScript, p_dict : Dictionary) -> ChannelPresenceEvent: return _safe_ret(NakamaSerializer.deserialize(p_ns, "ChannelPresenceEvent", p_dict), ChannelPresenceEvent) as ChannelPresenceEvent static func get_result_key() -> String: return "channel_presence_event" # Describes an error which occurred on the server. class Error extends NakamaAsyncResult: const _SCHEMA = { "code": {"name": "code", "type": TYPE_INT, "required": true}, "message": {"name": "message", "type": TYPE_STRING, "required": true}, "context": {"name": "context", "type": TYPE_DICTIONARY, "required": false, "content": TYPE_STRING}, } # The selection of possible error codes. enum Code { # An unexpected result from the server. RUNTIME_EXCEPTION = 0, # The server received a message which is not recognised. UNRECOGNIZED_PAYLOAD = 1, # A message was expected but contains no content. MISSING_PAYLOAD = 2, # Fields in the message have an invalid format. BAD_INPUT = 3, # The match id was not found. MATCH_NOT_FOUND = 4, # The match join was rejected. MATCH_JOIN_REJECTED = 5, # The runtime function does not exist on the server. RUNTIME_FUNCTION_NOT_FOUND = 6, #The runtime function executed with an error. RUNTIME_FUNCTION_EXCEPTION = 7, } # The error code which should be one of "Error.Code" enums. var code : int # A message in English to help developers debug the response. var message : String # Additional error details which may be different for each response. var context : Dictionary func _init(p_ex = null): super(p_ex) func _to_string(): if is_exception(): return get_exception()._to_string() return "Error" % [code, message, context] static func create(p_ns : GDScript, p_dict : Dictionary) -> Error: return _safe_ret(NakamaSerializer.deserialize(p_ns, "Error", p_dict), Error) as Error static func get_result_key() -> String: return "error" # A multiplayer match. class Match extends NakamaAsyncResult: const _SCHEMA = { "authoritative": {"name": "authoritative", "type": TYPE_BOOL, "required": false}, "match_id": {"name": "match_id", "type": TYPE_STRING, "required": true}, "label": {"name": "label", "type": TYPE_STRING, "required": false}, "presences": {"name": "presences", "type": TYPE_ARRAY, "required": false, "content": "UserPresence"}, "size": {"name": "size", "type": TYPE_INT, "required": false}, "self": {"name": "self_user", "type": "UserPresence", "required": true} } # If this match has an authoritative handler on the server. var authoritative : bool # The unique match identifier. var match_id : String # A label for the match which can be filtered on. var label : String # The presences already in the match. var presences : Array # UserPresence # The number of users currently in the match. var size : int # The current user in this match. i.e. Yourself. var self_user : UserPresence func _init(p_ex = null): super(p_ex) static func create(p_ns : GDScript, p_dict : Dictionary): return _safe_ret(NakamaSerializer.deserialize(p_ns, "Match", p_dict), Match) as Match func _to_string(): if is_exception(): return get_exception()._to_string() return "Match" % [authoritative, match_id, label, presences, size, self_user] static func get_result_key() -> String: return "match" # Some game state update in a match. class MatchData extends NakamaAsyncResult: const _SCHEMA = { "match_id": {"name": "match_id", "type": TYPE_STRING, "required": true}, "presence": {"name": "presence", "type": "UserPresence", "required": false}, "op_code": {"name": "op_code", "type": TYPE_INT, "required": false}, "data": {"name": "data", "type": TYPE_STRING, "required": false} } # The unique match identifier. var match_id : String # The operation code for the state change. # This value can be used to mark the type of the contents of the state. var op_code : int = 0 # The user that sent this game state update. var presence : UserPresence # The raw base64-encoded contents of the state change. var base64_data : String # The contents of the state change decoded as a UTF-8 string. var _data var data : String: get: if _data == null and base64_data != '': _data = Marshalls.base64_to_utf8(base64_data) return _data if _data != null else '' set(v): _data = v # The contents of the state change decoded as binary data. var _binary_data var binary_data : PackedByteArray: get: if _binary_data == null and base64_data != '': _binary_data = Marshalls.base64_to_raw(base64_data) return _binary_data func _init(p_ex = null): super(p_ex) func _to_string(): if is_exception(): return get_exception()._to_string() return "MatchData" % [match_id, op_code, presence, data] static func create(p_ns : GDScript, p_dict : Dictionary) -> MatchData: var out = _safe_ret(NakamaSerializer.deserialize(p_ns, "MatchData", p_dict), MatchData) as MatchData # Store the base64 data, ready to be decoded when the developer requests it. if out._data != null: out.base64_data = out._data out._data = null return out static func get_result_key() -> String: return "match_data" # A batch of join and leave presences for a match. class MatchPresenceEvent extends NakamaAsyncResult: const _SCHEMA = { "match_id": {"name": "match_id", "type": TYPE_STRING, "required": true}, "joins": {"name": "joins", "type": TYPE_ARRAY, "required": false, "content" : "UserPresence"}, "leaves": {"name": "leaves", "type": TYPE_ARRAY, "required": false, "content" : "UserPresence"}, } # Presences of users who joined the match. var joins : Array # Presences of users who left the match. var leaves : Array # The unique match identifier. var match_id : String func _init(p_ex = null): super(p_ex) func _to_string(): if is_exception(): return get_exception()._to_string() return "MatchPresenceEvent" % [match_id, joins, leaves] static func create(p_ns : GDScript, p_dict : Dictionary) -> MatchPresenceEvent: return _safe_ret(NakamaSerializer.deserialize(p_ns, "MatchPresenceEvent", p_dict), MatchPresenceEvent) as MatchPresenceEvent static func get_result_key() -> String: return "match_presence_event" # The result of a successful matchmaker operation sent to the server. class MatchmakerMatched extends NakamaAsyncResult: const _SCHEMA = { "match_id": {"name": "match_id", "type": TYPE_STRING, "required": false}, "ticket": {"name": "ticket", "type": TYPE_STRING, "required": true}, "token": {"name": "token", "type": TYPE_STRING, "required": false}, "users": {"name": "users", "type": TYPE_ARRAY, "required": false, "content": "MatchmakerUser"}, "self": {"name": "self_user", "type": "MatchmakerUser", "required": true} } # The id used to join the match. # A match ID used to join the match. var match_id : String # The ticket sent by the server when the user requested to matchmake for other players. var ticket : String # The token used to join a match. var token : String # The other users matched with this user and the parameters they sent. var users : Array # MatchmakerUser # The current user who matched with opponents. var self_user : MatchmakerUser func _init(p_ex = null): super(p_ex) func _to_string(): if is_exception(): return get_exception()._to_string() return "" % [ match_id, ticket, token, users, self_user ] static func create(p_ns : GDScript, p_dict : Dictionary) -> MatchmakerMatched: return _safe_ret(NakamaSerializer.deserialize(p_ns, "MatchmakerMatched", p_dict), MatchmakerMatched) as MatchmakerMatched static func get_result_key() -> String: return "matchmaker_matched" # The matchmaker ticket received from the server. class MatchmakerTicket extends NakamaAsyncResult: const _SCHEMA = { "ticket": {"name": "ticket", "type": TYPE_STRING, "required": true} } # The ticket generated by the matchmaker. var ticket : String func _init(p_ex = null): super(p_ex) static func create(p_ns : GDScript, p_dict : Dictionary) -> MatchmakerTicket: return _safe_ret(NakamaSerializer.deserialize(p_ns, "MatchmakerTicket", p_dict), MatchmakerTicket) as MatchmakerTicket func _to_string(): if is_exception(): return get_exception()._to_string() return "" % ticket static func get_result_key() -> String: return "matchmaker_ticket" # The user with the parameters they sent to the server when asking for opponents. class MatchmakerUser extends NakamaAsyncResult: const _SCHEMA = { "presence": {"name": "presence", "type": "UserPresence", "required": true}, "party_id": {"name": "party_id", "type": TYPE_STRING, "required": false}, "string_properties": {"name": "string_properties", "type": TYPE_DICTIONARY, "required": false, "content": TYPE_STRING}, "numeric_properties": {"name": "numeric_properties", "type": TYPE_DICTIONARY, "required": false, "content": TYPE_FLOAT}, } # The presence of the user. var presence : UserPresence # Party identifier, if this user was matched as a party member. var party_id : String # The numeric properties which this user asked to matchmake with. var numeric_properties : Dictionary # The string properties which this user asked to matchmake with. var string_properties : Dictionary func _init(p_ex = null): super(p_ex) func _to_string(): if is_exception(): return get_exception()._to_string() return "" % [ presence, numeric_properties, string_properties] static func create(p_ns : GDScript, p_dict : Dictionary) -> MatchmakerUser: return _safe_ret(NakamaSerializer.deserialize(p_ns, "MatchmakerUser", p_dict), MatchmakerUser) as MatchmakerUser static func get_result_key() -> String: return "matchmaker_user" # Receive status updates for users. class Status extends NakamaAsyncResult: const _SCHEMA = { "presences": {"name": "presences", "type": TYPE_ARRAY, "required": true, "content": "UserPresence"}, } # The status events for the users followed. var presences := Array() func _init(p_ex = null): super(p_ex) static func create(p_ns : GDScript, p_dict : Dictionary) -> Status: return _safe_ret(NakamaSerializer.deserialize(p_ns, "Status", p_dict), Status) as Status func _to_string(): if is_exception(): return get_exception()._to_string() return "" % [presences] static func get_result_key() -> String: return "status" # A status update event about other users who've come online or gone offline. class StatusPresenceEvent extends NakamaAsyncResult: const _SCHEMA = { "joins": {"name": "joins", "type": TYPE_ARRAY, "required": false, "content" : "UserPresence"}, "leaves": {"name": "leaves", "type": TYPE_ARRAY, "required": false, "content" : "UserPresence"}, } # Presences of users who joined the server. # This join information is in response to a subscription made to be notified when a user comes online. var joins : Array # Presences of users who left the server. # This leave information is in response to a subscription made to be notified when a user goes offline. var leaves : Array func _init(p_ex = null): super(p_ex) func _to_string(): if is_exception(): return get_exception()._to_string() return "StatusPresenceEvent" % [joins, leaves] static func create(p_ns : GDScript, p_dict : Dictionary) -> StatusPresenceEvent: return _safe_ret(NakamaSerializer.deserialize(p_ns, "StatusPresenceEvent", p_dict), StatusPresenceEvent) as StatusPresenceEvent static func get_result_key() -> String: return "status_presence_event" # A realtime socket stream on the server. class Stream extends NakamaAsyncResult: const _SCHEMA = { "mode": {"name": "mode", "type": TYPE_INT, "required": true}, "subject": {"name": "subject", "type": TYPE_STRING, "required": false}, "subcontext": {"name": "subcontext", "type": TYPE_STRING, "required": false}, "label": {"name": "label", "type": TYPE_STRING, "required": false}, } # The mode of the stream. var mode : int # The subject of the stream. This is usually a user id. var subject : String # The descriptor of the stream. Used with direct chat messages and contains a second user id. var subcontext : String # Identifies streams which have a context across users like a chat channel room. var label : String func _init(p_ex = null): super(p_ex) func _to_string(): if is_exception(): return get_exception()._to_string() return "Stream" % [mode, subject, subcontext, label] static func create(p_ns : GDScript, p_dict : Dictionary) -> Stream: return _safe_ret(NakamaSerializer.deserialize(p_ns, "Stream", p_dict), Stream) as Stream static func get_result_key() -> String: return "stream" # A batch of joins and leaves on the low level stream. # Streams are built on to provide abstractions for matches, chat channels, etc. In most cases you'll never need to # interact with the low level stream itself. class StreamPresenceEvent extends NakamaAsyncResult: const _SCHEMA = { "stream": {"name": "stream", "type": "Stream", "required": true}, "joins": {"name": "joins", "type": TYPE_ARRAY, "required": false, "content" : "UserPresence"}, "leaves": {"name": "leaves", "type": TYPE_ARRAY, "required": false, "content" : "UserPresence"}, } # Presences of users who left the stream. var joins : Array # Presences of users who joined the stream. var leaves : Array # The identifier for the stream. var stream : Stream = null func _to_string(): if is_exception(): return get_exception()._to_string() return "StreamPresenceEvent" % [stream, joins, leaves] static func create(p_ns : GDScript, p_dict : Dictionary) -> StreamPresenceEvent: return _safe_ret(NakamaSerializer.deserialize(p_ns, "StreamPresenceEvent", p_dict), StreamPresenceEvent) as StreamPresenceEvent static func get_result_key() -> String: return "stream_presence_event" # A state change received from a stream. class StreamData extends NakamaAsyncResult: const _SCHEMA = { "stream": {"name": "stream", "type": "Stream", "required": true}, "sender": {"name": "sender", "type": "UserPresence", "required": false}, "data": {"name": "state", "type": TYPE_STRING, "required": false}, "reliable": {"name": "reliable", "type": TYPE_BOOL, "required": false}, } # The user who sent the state change. May be `null`. var sender : UserPresence = null # The contents of the state change. var state : String # The identifier for the stream. var stream : Stream # True if this data was delivered reliably, false otherwise. var reliable : bool func _to_string(): if is_exception(): return get_exception()._to_string() return "StreamData" % [sender, state, stream] static func create(p_ns : GDScript, p_dict : Dictionary) -> StreamData: return _safe_ret(NakamaSerializer.deserialize(p_ns, "StreamData", p_dict), StreamData) as StreamData static func get_result_key() -> String: return "stream_data" # An object which represents a connected user in the server. # The server allows the same user to be connected with multiple sessions. To uniquely identify them a tuple of # `{ node_id, user_id, session_id }` is used which is exposed as this object. class UserPresence extends NakamaAsyncResult: const _SCHEMA = { "persistence": {"name": "persistence", "type": TYPE_BOOL, "required": false}, "session_id": {"name": "session_id", "type": TYPE_STRING, "required": true}, "status": {"name": "status", "type": TYPE_STRING, "required": false}, "username": {"name": "username", "type": TYPE_STRING, "required": false}, "user_id": {"name": "user_id", "type": TYPE_STRING, "required": true}, } # If this presence generates stored events like persistent chat messages or notifications. var persistence : bool # The session id of the user. var session_id : String # The status of the user with the presence on the server. var status : String # The username for the user. var username : String # The id of the user. var user_id : String func _init(p_ex = null): super(p_ex) func serialize() -> Dictionary: return NakamaSerializer.serialize(self) func _to_string(): if is_exception(): return get_exception()._to_string() return "UserPresence" % [ persistence, session_id, status, username, user_id] static func create(p_ns : GDScript, p_dict : Dictionary) -> UserPresence: return _safe_ret(NakamaSerializer.deserialize(p_ns, "UserPresence", p_dict), UserPresence) as UserPresence static func get_result_key() -> String: return "user_presence" class Party extends NakamaAsyncResult: const _SCHEMA = { "party_id": {"name": "party_id", "type": TYPE_STRING, "required": true}, "open": {"name": "open", "type": TYPE_BOOL, "required": false}, "max_size": {"name": "max_size", "type": TYPE_INT, "required": true}, "self": {"name": "self_presence", "type": "UserPresence", "required": true}, "leader": {"name": "leader", "type": "UserPresence", "required": true}, "presences": {"name": "presences", "type": TYPE_ARRAY, "required": false, "content": "UserPresence"}, } # Unique party identifier. var party_id : String # Open flag. var open : bool = false # Maximum number of party members. var max_size : int # The presence of the current user. i.e. Your self. var self_presence : NakamaRTAPI.UserPresence # Leader. var leader : NakamaRTAPI.UserPresence # All current party members. var presences : Array # of objects NakamaUserPresence func _init(p_ex = null): super(p_ex) func serialize() -> Dictionary: return NakamaSerializer.serialize(self) func _to_string(): if is_exception(): return get_exception()._to_string() return "Party" % [ party_id, open, max_size, self_presence, leader, presences] static func create(p_ns : GDScript, p_dict : Dictionary) -> Party: return _safe_ret(NakamaSerializer.deserialize(p_ns, "Party", p_dict), Party) as Party static func get_result_key() -> String: return "party" # Presence update for a particular party. class PartyPresenceEvent extends NakamaAsyncResult: const _SCHEMA = { "party_id": {"name": "party_id", "type": TYPE_STRING, "required": true}, "joins": {"name": "joins", "type": TYPE_ARRAY, "required": false, "content": "UserPresence"}, "leaves": {"name": "leaves", "type": TYPE_ARRAY, "required": false, "content": "UserPresence"}, } # The party ID. var party_id : String # User presences that have just joined the party. var joins : Array # User presences that have just left the party. var leaves : Array func _init(p_ex = null): super(p_ex) func serialize() -> Dictionary: return NakamaSerializer.serialize(self) func _to_string(): if is_exception(): return get_exception()._to_string() return "PartyPresenceEvent" % [party_id, joins, leaves] static func create(p_ns : GDScript, p_dict : Dictionary) -> PartyPresenceEvent: return _safe_ret(NakamaSerializer.deserialize(p_ns, "PartyPresenceEvent", p_dict), PartyPresenceEvent) as PartyPresenceEvent static func get_result_key() -> String: return "party_presence_event" # Announcement of a new party leader. class PartyLeader extends NakamaAsyncResult: const _SCHEMA = { "party_id": {"name": "party_id", "type": TYPE_STRING, "required": true}, "presence": {"name": "presence", "type": "UserPresence", "required": true}, } # Party ID to promote a new leader for. var party_id : String # The presence of an existing party member to promote as the new leader. var presence : NakamaRTAPI.UserPresence func _init(p_ex = null): super(p_ex) func serialize() -> Dictionary: return NakamaSerializer.serialize(self) func _to_string(): if is_exception(): return get_exception()._to_string() return "PartyLeader" % [party_id, presence] static func create(p_ns : GDScript, p_dict : Dictionary) -> PartyLeader: return _safe_ret(NakamaSerializer.deserialize(p_ns, "PartyLeader", p_dict), PartyLeader) as PartyLeader static func get_result_key() -> String: return "party_leader" # Incoming notification for one or more new presences attempting to join the party. class PartyJoinRequest extends NakamaAsyncResult: const _SCHEMA = { "party_id": {"name": "party_id", "type": TYPE_STRING, "required": true}, "presences": {"name": "presences", "type": TYPE_ARRAY, "required": false, "content": "UserPresence"}, } # Party ID these presences are attempting to join. var party_id : String # Presences attempting to join. var presences : Array func _init(p_ex = null): super(p_ex) func serialize() -> Dictionary: return NakamaSerializer.serialize(self) func _to_string(): if is_exception(): return get_exception()._to_string() return "PartyJoinRequest" % [party_id, presences] static func create(p_ns : GDScript, p_dict : Dictionary) -> PartyJoinRequest: return _safe_ret(NakamaSerializer.deserialize(p_ns, "PartyJoinRequest", p_dict), PartyJoinRequest) as PartyJoinRequest static func get_result_key() -> String: return "party_join_request" # A response from starting a new party matchmaking process. class PartyMatchmakerTicket extends NakamaAsyncResult: const _SCHEMA = { "party_id": {"name": "party_id", "type": TYPE_STRING, "required": true}, "ticket": {"name": "ticket", "type": TYPE_STRING, "required": true}, } # Party ID. var party_id : String # The ticket that can be used to cancel matchmaking. var ticket : String func _init(p_ex = null): super(p_ex) func serialize() -> Dictionary: return NakamaSerializer.serialize(self) func _to_string(): if is_exception(): return get_exception()._to_string() return "PartyMatchmakerTicket" % [party_id, ticket] static func create(p_ns : GDScript, p_dict : Dictionary) -> PartyMatchmakerTicket: return _safe_ret(NakamaSerializer.deserialize(p_ns, "PartyMatchmakerTicket", p_dict), PartyMatchmakerTicket) as PartyMatchmakerTicket static func get_result_key() -> String: return "party_matchmaker_ticket" # Incoming party data delivered from the server. class PartyData extends NakamaAsyncResult: const _SCHEMA = { "party_id": {"name": "party_id", "type": TYPE_STRING, "required": true}, "presence": {"name": "presence", "type": "UserPresence", "required": false}, "op_code": {"name": "op_code", "type": TYPE_INT, "required": true}, "data": {"name": "data", "type": TYPE_STRING, "required": false} } # The party ID. var party_id : String # A reference to the user presence that sent this data, if any. var presence : NakamaRTAPI.UserPresence # Op code value. var op_code : int # The raw base64-encoded contents of the state change. var base64_data : String # The contents of the state change decoded as a UTF-8 string. var _data var data : String: get: if _data == null and base64_data != '': _data = Marshalls.base64_to_utf8(base64_data) return _data if _data != null else '' set(v): _data = v # The contents of the state change decoded as binary data. var _binary_data var binary_data : PackedByteArray: get: if _binary_data == null and base64_data != '': _binary_data = Marshalls.base64_to_raw(base64_data) return _binary_data func _init(p_ex = null): super(p_ex) func serialize() -> Dictionary: return NakamaSerializer.serialize(self) func _to_string(): if is_exception(): return get_exception()._to_string() return "PartyData" % [party_id, presence, op_code, data] static func create(p_ns : GDScript, p_dict : Dictionary) -> PartyData: var out := _safe_ret(NakamaSerializer.deserialize(p_ns, "PartyData", p_dict), PartyData) as PartyData # Store the base64 data, ready to be decoded when the developer requests it. if out._data != null: out.base64_data = out._data out._data = null return out static func get_result_key() -> String: return "party_data" # End a party, kicking all party members and closing it. (this is both a message and a result) class PartyClose extends NakamaAsyncResult: const _SCHEMA = { "party_id": {"name": "party_id", "type": TYPE_STRING, "required": true}, } # Party ID to close. var party_id : String func _init(p_ex = null): super(p_ex) func serialize(): return NakamaSerializer.serialize(self) func get_msg_key() -> String: return "party_close" func _to_string(): if is_exception(): return get_exception()._to_string() return "PartyClose" % [party_id] static func create(p_ns : GDScript, p_dict : Dictionary) -> PartyClose: return _safe_ret(NakamaSerializer.deserialize(p_ns, "PartyClose", p_dict), PartyClose) as PartyClose static func get_result_key() -> String: return "party_close" ================================================ FILE: addons/com.heroiclabs.nakama/api/NakamaRTMessage.gd ================================================ extends RefCounted class_name NakamaRTMessage # Send a channel join message to the server. class ChannelJoin: const _SCHEMA = { "persistence": {"name": "persistence", "type": TYPE_BOOL, "required": true}, "hidden": {"name": "hidden", "type": TYPE_BOOL, "required": true}, "target": {"name": "target", "type": TYPE_STRING, "required": true}, "type": {"name": "type", "type": TYPE_INT, "required": true}, } enum ChannelType { # A chat room which can be created dynamically with a name. Room = 1, # A private chat between two users. DirectMessage = 2, # A chat within a group on the server. Group = 3 } var persistence : bool var hidden : bool var target : String var type : int func _init(p_target : String, p_type : int, p_persistence : bool, p_hidden : bool): persistence = p_persistence hidden = p_hidden target = p_target type = p_type if p_type >= ChannelType.Room and p_type <= ChannelType.Group else 0 # Will cause error server side func serialize() -> Dictionary: return NakamaSerializer.serialize(self) func get_msg_key() -> String: return "channel_join" func _to_string(): return "ChannelJoin