agones.dev/agones@v1.54.0/sdks/unity/AgonesBetaSdk.cs (about)

     1  // Copyright 2022 Google LLC
     2  // All Rights Reserved.
     3  //
     4  // Licensed under the Apache License, Version 2.0 (the "License");
     5  // you may not use this file except in compliance with the License.
     6  // You may obtain a copy of the License at
     7  //
     8  //     http://www.apache.org/licenses/LICENSE-2.0
     9  //
    10  // Unless required by applicable law or agreed to in writing, software
    11  // distributed under the License is distributed on an "AS IS" BASIS,
    12  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  // See the License for the specific language governing permissions and
    14  // limitations under the License.
    15  
    16  using System;
    17  using System.Collections.Generic;
    18  using System.Linq;
    19  using System.Net;
    20  using System.Runtime.CompilerServices;
    21  using System.Text;
    22  using System.Threading;
    23  using System.Threading.Tasks;
    24  using Agones.Model;
    25  using JetBrains.Annotations;
    26  using MiniJSON;
    27  using UnityEngine;
    28  using UnityEngine.Networking;
    29  
    30  namespace Agones
    31  {
    32      /// <summary>
    33      /// Agones Beta SDK for Unity.
    34      /// </summary>
    35      public class AgonesBetaSdk : AgonesSdk
    36      {
    37          #region AgonesRestClient Public Methods
    38          
    39          /// <summary>
    40          /// GetCounterCountAsync returns the Count for a Counter, given the Counter's key (name).
    41          /// Always returns 0 if the key was not predefined in the GameServer resource on creation.
    42          /// </summary>
    43          /// <returns>The counter's count</returns>
    44          public async Task<long> GetCounterCount(string key)
    45          {
    46              var result = await SendRequestAsync($"/v1beta1/counters/{key}", "{}", UnityWebRequest.kHttpVerbGET);
    47              if (!result.ok)
    48              {
    49                  return 0;
    50              }
    51  
    52              if (Json.Deserialize(result.json) is not Dictionary<string, object> data
    53                  || !data.TryGetValue("count", out object countObject)
    54                  || countObject is not string countString
    55                  || !long.TryParse(countString, out long count))
    56              {
    57                  return 0;
    58              }
    59  
    60              return count;
    61          }
    62          
    63          private struct CounterUpdateRequest
    64          {
    65              public long countDiff;
    66          }
    67  
    68          /// <summary>
    69          /// IncrementCounterAsync increases a counter by the given nonnegative integer amount.
    70          /// Will execute the increment operation against the current CRD value. Will max at max(int64).
    71          /// Throws error if the key was not predefined in the GameServer resource on creation.
    72          /// Throws error if the count is at the current capacity (to the latest knowledge of the SDK),
    73          /// and no increment will occur.
    74          ///
    75          /// Note: A potential race condition here is that if count values are set from both the SDK and
    76          /// through the K8s API (Allocation or otherwise), since the SDK append operation back to the CRD
    77          /// value is batched asynchronous any value incremented past the capacity will be silently truncated.
    78          /// </summary>
    79          /// <returns>
    80          /// A task that represents the asynchronous operation and returns true if the request was successful.
    81          /// </returns>
    82          public async Task<bool> IncrementCounter(string key, long amount)
    83          {
    84              if (amount < 0)
    85              {
    86                  throw new ArgumentOutOfRangeException($"CountIncrement amount must be a positive number, found {amount}");
    87              }
    88              
    89              string json = JsonUtility.ToJson(new CounterUpdateRequest {countDiff = amount });
    90              return await SendRequestAsync($"/v1beta1/counters/{key}", json, "PATCH").ContinueWith(task => task.Result.ok);
    91          }
    92  
    93          /// <summary>
    94          /// DecrementCounterAsync decreases the current count by the given nonnegative integer amount.
    95          /// The Counter will not go below 0. Will execute the decrement operation against the current CRD value.
    96          /// Throws error if the count is at 0 (to the latest knowledge of the SDK), and no decrement will occur.
    97          /// </summary>
    98          /// <returns>
    99          /// A task that represents the asynchronous operation and returns true if the request was successful.
   100          /// </returns>
   101          public async Task<bool> DecrementCounter(string key, long amount)
   102          {
   103              if (amount < 0)
   104              {
   105                  throw new ArgumentOutOfRangeException($"CountIncrement amount must be a positive number, found {amount}");
   106              }
   107              
   108              string json = JsonUtility.ToJson(new CounterUpdateRequest {countDiff = amount * -1});
   109              return await SendRequestAsync($"/v1beta1/counters/{key}", json, "PATCH").ContinueWith(task => task.Result.ok);
   110          }
   111  
   112          private struct CounterSetRequest {
   113              public long count;
   114          }
   115  
   116          /// <summary>
   117          /// SetCounterCountAsync sets a count to the given value. Use with care, as this will
   118          /// overwrite any previous invocations’ value. Cannot be greater than Capacity.
   119          /// </summary>
   120          /// <returns>
   121          /// A task that represents the asynchronous operation and returns true if the request was successful.
   122          /// </returns>
   123          public async Task<bool> SetCounterCount(string key, long amount)
   124          {
   125              string json = JsonUtility.ToJson(new CounterSetRequest {count = amount});
   126              return await SendRequestAsync($"/v1beta1/counters/{key}", json, "PATCH").ContinueWith(task => task.Result.ok);
   127          }
   128  
   129          /// <summary>
   130          /// GetCounterCapacityAsync returns the Capacity for a Counter, given the Counter's key (name).
   131          /// Always returns 0 if the key was not predefined in the GameServer resource on creation.
   132          /// </summary>
   133          /// <returns>The Counter's capacity</returns>
   134          public async Task<long> GetCounterCapacity(string key)
   135          {
   136              var result =  await SendRequestAsync($"/v1beta1/counters/{key}", "{}", UnityWebRequest.kHttpVerbGET);
   137              if (!result.ok)
   138              {
   139                  return 0;
   140              }
   141  
   142              if (Json.Deserialize(result.json) is not Dictionary<string, object> data
   143                  || !data.TryGetValue("capacity", out object capacityObject)
   144                  || capacityObject is not string capacityString
   145                  || !long.TryParse(capacityString, out long capacity))
   146              {
   147                  return 0;
   148              }
   149  
   150              return capacity;
   151          }
   152  
   153          private struct CounterSetCapacityRequest {
   154              public long capacity;
   155          }
   156  
   157          /// <summary>
   158          /// SetCounterCapacityAsync sets the capacity for the given Counter.
   159          /// A capacity of 0 is no capacity.
   160          /// </summary>
   161          /// <returns>
   162          /// A task that represents the asynchronous operation and returns true if the request was successful.
   163          /// </returns>
   164          public async Task<bool> SetCounterCapacity(string key, long amount)
   165          {
   166              string json = JsonUtility.ToJson(new CounterSetCapacityRequest {capacity = amount});
   167              return await SendRequestAsync($"/v1beta1/counters/{key}", json, "PATCH").ContinueWith(task => task.Result.ok);
   168          }
   169  
   170          /// <summary>
   171          /// GetListCapacityAsync returns the Capacity for a List, given the List's key (name).
   172          /// Always returns 0 if the key was not predefined in the GameServer resource on creation.
   173          /// </summary>
   174          /// <returns>The List's capacity</returns>
   175          public async Task<long> GetListCapacity(string key)
   176          {
   177              var result =  await SendRequestAsync($"/v1beta1/lists/{key}", "{}", UnityWebRequest.kHttpVerbGET);
   178              if (!result.ok)
   179              {
   180                  return 0;
   181              }
   182  
   183              if (Json.Deserialize(result.json) is not Dictionary<string, object> data
   184                  || !data.TryGetValue("capacity", out object capacityObject)
   185                  || capacityObject is not string capacityString
   186                  || !long.TryParse(capacityString, out long capacity))
   187              {
   188                  return 0;
   189              }
   190  
   191              return capacity;
   192          }
   193  
   194          private struct ListSetCapacityRequest {
   195              public long capacity;
   196          }
   197  
   198          /// <summary>
   199          /// SetListCapacityAsync sets the capacity for a given list. Capacity must be between 0 and 1000.
   200          /// Always returns false if the key was not predefined in the GameServer resource on creation.
   201          /// </summary>
   202          /// <returns>
   203          /// A task that represents the asynchronous operation and returns true if the request was successful.
   204          /// </returns>
   205          public async Task<bool> SetListCapacity(string key, long amount)
   206          {
   207              string json = JsonUtility.ToJson(new ListSetCapacityRequest {
   208                  capacity = amount
   209              });
   210              return await SendRequestAsync($"/v1beta1/lists/{key}", json, "PATCH").ContinueWith(task => task.Result.ok);
   211          }
   212  
   213          /// <summary>
   214          /// ListContainsAsync returns if a string exists in a List's values list, given the List's key
   215          /// and the string value. Search is case-sensitive.
   216          /// Always returns false if the key was not predefined in the GameServer resource on creation.
   217          /// </summary>
   218          /// <returns>True if the value is found in the List</returns>
   219          public async Task<bool> ListContains(string key, string value)
   220          {
   221              var result =  await SendRequestAsync($"/v1beta1/lists/{key}", "{}", UnityWebRequest.kHttpVerbGET);
   222              
   223              if (!result.ok)
   224              {
   225                  return false;
   226              }
   227  
   228              if (Json.Deserialize(result.json) is not Dictionary<string, object> data
   229                  || !data.TryGetValue("values", out object listObject)
   230                  || listObject is not List<object> list)
   231              {
   232                  return false;
   233              }
   234              
   235              return list.Where(l => l is string).Select(l => l.ToString()).Contains(value);
   236          }
   237  
   238          /// <summary>
   239          /// GetListLengthAsync returns the length of the Values list for a List, given the List's key.
   240          /// Always returns 0 if the key was not predefined in the GameServer resource on creation.
   241          /// </summary>
   242          /// <returns>The length of List's values array</returns>
   243          public async Task<int> GetListLength(string key)
   244          {
   245              var result =  await SendRequestAsync($"/v1beta1/lists/{key}", "{}", UnityWebRequest.kHttpVerbGET);
   246              
   247              if (!result.ok)
   248              {
   249                  return 0;
   250              }
   251  
   252              if (Json.Deserialize(result.json) is not Dictionary<string, object> data
   253                  || !data.TryGetValue("values", out object listObject)
   254                  || listObject is not List<object> list)
   255              {
   256                  return 0;
   257              }
   258              
   259              return list.Count();
   260          }
   261  
   262          /// <summary>
   263          /// GetListValuesAsync returns the Values for a List, given the List's key (name).
   264          /// Always returns an empty list if the key was not predefined in the GameServer resource on creation.
   265          /// </summary>
   266          /// <returns>The List's values array</returns>
   267          public async Task<List<string>> GetListValues(string key)
   268          {
   269              var result =  await SendRequestAsync($"/v1beta1/lists/{key}", "{}", UnityWebRequest.kHttpVerbGET);
   270              
   271              if (!result.ok)
   272              {
   273                  return new List<string>();
   274              }
   275  
   276              if (Json.Deserialize(result.json) is not Dictionary<string, object> data
   277                  || !data.TryGetValue("values", out object listObject)
   278                  || listObject is not List<object> list)
   279              {
   280                  return new List<string>();
   281              }
   282              
   283              return list.Where(l => l is string).Select(l => l.ToString()).ToList();
   284          }
   285          
   286          private struct ListUpdateValuesRequest
   287          {
   288              public string value;
   289          }
   290  
   291          /// <summary>
   292          /// AppendListValueAsync appends a string to a List's values list, given the List's key (name)
   293          /// and the string value. Throws error if the string already exists in the list.
   294          /// Always returns false if the key was not predefined in the GameServer resource on creation.
   295          /// </summary>
   296          /// <returns>
   297          /// A task that represents the asynchronous operation and returns true if the request was successful.
   298          /// </returns>
   299          public async Task<bool> AppendListValue(string key, string value)
   300          {
   301              string json = JsonUtility.ToJson(new ListUpdateValuesRequest {value = value});
   302              return await SendRequestAsync($"/v1beta1/lists/{key}:addValue", json, "POST").ContinueWith(task => task.Result.ok);
   303          }
   304  
   305          /// <summary>
   306          /// DeleteListValueAsync removes a string from a List's values list, given the List's key
   307          /// and the string value. Throws error if the string does not exist in the list.
   308          /// Always returns false if the key was not predefined in the GameServer resource on creation.
   309          /// </summary>
   310          /// <returns>
   311          /// A task that represents the asynchronous operation and returns true if the request was successful.
   312          /// </returns>
   313          public async Task<bool> DeleteListValue(string key, string value)
   314          {
   315              string json = JsonUtility.ToJson(new ListUpdateValuesRequest {value = value});
   316              return await SendRequestAsync($"/v1beta1/lists/{key}:removeValue", json, "POST").ContinueWith(task => task.Result.ok);
   317          }
   318  
   319          #endregion
   320  
   321      }
   322  }