diff --git a/Nakama+Hiro/definitions/dev1/base-teams.json b/Nakama+Hiro/definitions/dev1/base-teams.json new file mode 100644 index 00000000..50f57142 --- /dev/null +++ b/Nakama+Hiro/definitions/dev1/base-teams.json @@ -0,0 +1,101 @@ +{ + "initial_max_team_size": 30, + "max_team_size": 50, + "allow_multiple_teams": false, + "wallet": { + "currencies": { + "TeamCoins": 0 + } + }, + "stats": { + "stats_public": { + "wins": { "value": 0 }, + "level": { "value": 1 }, + "totalScore": { "value": 0 } + }, + "stats_private": { + "internal_rating": { "value": 1000 } + } + }, + "gifts": { + "gifts": { + "daily_gift": { + "name": "Daily Team Gift", + "description": "Contribute together for rewards!", + "category": "daily", + "max_count": 100, + "max_contributor_count": 5, + "reset_schedule": "0 0 * * *", + "duration_sec": 86400, + "contribution_cost": { + "currencies": { + "TeamCoins": { "min": 10 } + } + }, + "contribution_reward": { + "guaranteed": { + "currencies": { + "TeamCoins": { "min": 5 } + } + } + }, + "rewards": [ + { + "min_count": 25, + "contributor_reward": { + "guaranteed": { + "currencies": { + "TeamCoins": { "min": 50 } + } + } + }, + "noncontributor_reward": { + "guaranteed": { + "currencies": { + "TeamCoins": { "min": 25 } + } + } + } + }, + { + "min_count": 50, + "contributor_reward": { + "guaranteed": { + "currencies": { + "TeamCoins": { "min": 100 } + } + } + }, + "noncontributor_reward": { + "guaranteed": { + "currencies": { + "TeamCoins": { "min": 50 } + } + } + } + }, + { + "min_count": 100, + "contributor_reward": { + "guaranteed": { + "currencies": { + "TeamCoins": { "min": 200 } + } + } + }, + "noncontributor_reward": { + "guaranteed": { + "currencies": { + "TeamCoins": { "min": 100 } + } + } + } + } + ] + } + } + }, + "reward_mailbox": { + "max_size": 100 + } +} diff --git a/Nakama+Hiro/local.yml b/Nakama+Hiro/local.yml index f2312178..a5669968 100644 --- a/Nakama+Hiro/local.yml +++ b/Nakama+Hiro/local.yml @@ -2,13 +2,13 @@ console: max_message_size_bytes: 409600 leaderboard: blacklist_rank_cache: - - "*" + - "*" logger: level: "DEBUG" runtime: env: - - "ENV=dev1" - - "HIRO_LICENSE=" + - "ENV=dev1" + - "HIRO_LICENSE=" session: token_expiry_sec: 86400 # 24 hours refresh_token_expiry_sec: 604800 # 7 days diff --git a/Nakama+Hiro/main.go b/Nakama+Hiro/main.go index 7733d951..0d92f03a 100644 --- a/Nakama+Hiro/main.go +++ b/Nakama+Hiro/main.go @@ -64,7 +64,8 @@ func InitModule(ctx context.Context, logger runtime.Logger, db *sql.DB, nk runti hiro.WithLeaderboardsSystem(fmt.Sprintf("definitions/%s/base-leaderboards.json", env), true), hiro.WithChallengesSystem(fmt.Sprintf("definitions/%s/base-challenges.json", env), true), hiro.WithEconomySystem(fmt.Sprintf("definitions/%s/base-economy.json", env), true), - hiro.WithEventLeaderboardsSystem(fmt.Sprintf("definitions/%s/base-event-leaderboards.json", env), true)) + hiro.WithEventLeaderboardsSystem(fmt.Sprintf("definitions/%s/base-event-leaderboards.json", env), true), + hiro.WithTeamsSystem(fmt.Sprintf("definitions/%s/base-teams.json", env), true)) if err != nil { return err } diff --git a/UnityHiroChallenges/Assets/UnityHiroChallenges/Scripts/HiroChallengesCoordinator.cs b/UnityHiroChallenges/Assets/UnityHiroChallenges/Scripts/HiroChallengesCoordinator.cs index 89de4e01..0a4f8191 100644 --- a/UnityHiroChallenges/Assets/UnityHiroChallenges/Scripts/HiroChallengesCoordinator.cs +++ b/UnityHiroChallenges/Assets/UnityHiroChallenges/Scripts/HiroChallengesCoordinator.cs @@ -71,13 +71,6 @@ public static NakamaSystem.AuthorizerFunc NakamaAuthorizerFunc(int index = 0) return async client => { - // Due to the Account Switcher tool, we might need to log out before re-authenticating. - var nakamaSystem = Instance.GetSystem(); - if (nakamaSystem.Session != null) - { - await client.SessionLogoutAsync(nakamaSystem.Session); - } - // Attempt to load a previous session if it is still valid. var authToken = PlayerPrefs.GetString($"{playerPrefsAuthToken}_{index}"); var refreshToken = PlayerPrefs.GetString($"{playerPrefsRefreshToken}_{index}"); @@ -86,17 +79,11 @@ public static NakamaSystem.AuthorizerFunc NakamaAuthorizerFunc(int index = 0) // Add an hour, so we check whether the token is within an hour of expiration to refresh it. var expiredDate = DateTime.UtcNow.AddHours(1); - if (session != null && !session.HasRefreshExpired(expiredDate)) - { - return session; - } + if (session != null && !session.HasRefreshExpired(expiredDate)) return session; // Attempt to read the device ID to use for Authentication. var deviceId = PlayerPrefs.GetString(playerPrefsDeviceId, SystemInfo.deviceUniqueIdentifier); - if (deviceId == SystemInfo.unsupportedIdentifier) - { - deviceId = Guid.NewGuid().ToString(); - } + if (deviceId == SystemInfo.unsupportedIdentifier) deviceId = Guid.NewGuid().ToString(); session = await client.AuthenticateDeviceAsync($"{deviceId}_{index}"); @@ -105,10 +92,7 @@ public static NakamaSystem.AuthorizerFunc NakamaAuthorizerFunc(int index = 0) PlayerPrefs.SetString($"{playerPrefsAuthToken}_{index}", session.AuthToken); PlayerPrefs.SetString($"{playerPrefsRefreshToken}_{index}", session.RefreshToken); - if (session.Created) - { - Debug.LogFormat("New user account '{0}' created.", session.UserId); - } + if (session.Created) Debug.LogFormat("New user account '{0}' created.", session.UserId); return session; }; @@ -125,4 +109,4 @@ protected override void SystemsInitializeFailed(Exception e) ReceivedStartError?.Invoke(e); } } -} +} \ No newline at end of file diff --git a/UnityHiroTeams/.gitignore b/UnityHiroTeams/.gitignore new file mode 100644 index 00000000..ccf423be --- /dev/null +++ b/UnityHiroTeams/.gitignore @@ -0,0 +1,56 @@ + +# Created by https://www.gitignore.io/api/unity +# Edit at https://www.gitignore.io/?templates=unity + +# Jetbrain Rider Cache +.idea/ +Assets/Plugins/Editor/JetBrains* + +# Visual Studio Code +.vscode/ + + +### Unity ### +/[Ll]ibrary/ +/[Tt]emp/ +/[Oo]bj/ +/[Bb]uild/ +/[Bb]uilds/ +/[Ll]ogs/ +/[Uu]ser[Ss]ettings/ +Assets/AssetStoreTools* +# Unity local user project setting +UserSettings/ + +# Visual Studio cache directory +.vs/ + +# Autogenerated VS/MD/Consulo solution and project files +ExportedObj/ +.consulo/ +*.csproj +*.unityproj +*.sln +*.suo +*.tmp +*.user +*.userprefs +*.pidb +*.booproj +*.svd +*.pdb +*.opendb +*.VC.db + +# Unity3D generated meta files +*.pidb.meta +*.pdb.meta + +# Unity3D Generated File On Crash Reports +sysinfo.txt + +# Builds +*.apk +*.unitypackage + +# End of https://www.gitignore.io/api/unity \ No newline at end of file diff --git a/UnityHiroTeams/Assets/UnityHiroTeams.meta b/UnityHiroTeams/Assets/UnityHiroTeams.meta new file mode 100644 index 00000000..2a7b42cd --- /dev/null +++ b/UnityHiroTeams/Assets/UnityHiroTeams.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 7414ab557adee4a1a95af94631c66590 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityHiroTeams/Assets/UnityHiroTeams/Editor.meta b/UnityHiroTeams/Assets/UnityHiroTeams/Editor.meta new file mode 100644 index 00000000..8504a2c6 --- /dev/null +++ b/UnityHiroTeams/Assets/UnityHiroTeams/Editor.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: dd78068405c264cd6beb0ee55cfa0eda +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityHiroTeams/Assets/UnityHiroTeams/Editor/AccountSwitcher.cs b/UnityHiroTeams/Assets/UnityHiroTeams/Editor/AccountSwitcher.cs new file mode 100644 index 00000000..42e310bd --- /dev/null +++ b/UnityHiroTeams/Assets/UnityHiroTeams/Editor/AccountSwitcher.cs @@ -0,0 +1,219 @@ +// Copyright 2025 The Nakama Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using System.Collections.Generic; +using System.Text; +using Nakama; +using Hiro; +using Hiro.Unity; +using UnityEditor; +using UnityEngine; +using UnityEngine.SceneManagement; +using UnityEngine.UIElements; + +namespace HiroTeams.Editor +{ + public class AccountSwitcherEditor : EditorWindow + { + [SerializeField] private VisualTreeAsset tree; + + private DropdownField accountDropdown; + private Label usernamesLabel; + + private readonly SortedDictionary accountUsernames = new(); + + private const string AccountUsernamesKey = "AccountSwitcher_Usernames"; + + [MenuItem("Tools/Nakama/Account Switcher")] + public static void ShowWindow() + { + var inspectorType = typeof(UnityEditor.Editor).Assembly.GetType("UnityEditor.InspectorWindow"); + var window = GetWindow("Account Switcher", inspectorType); + + window.Focus(); + } + + [MenuItem("Tools/Nakama/Clear Test Accounts")] + public static void ClearSavedAccounts() + { + EditorPrefs.DeleteKey(AccountUsernamesKey); + Debug.Log("Cleared all saved account usernames"); + + // Refresh any open Account Switcher windows + var windows = Resources.FindObjectsOfTypeAll(); + foreach (var window in windows) + { + window.accountUsernames.Clear(); + window.UpdateUsernameLabels(); + } + } + + private void CreateGUI() + { + tree.CloneTree(rootVisualElement); + + accountDropdown = rootVisualElement.Q("account-dropdown"); + accountDropdown.RegisterValueChangedCallback(SwitchAccount); + + usernamesLabel = rootVisualElement.Q