Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
126 changes: 119 additions & 7 deletions Assets/Talo Game Services/Talo/Runtime/Entities/EntityWithProps.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq;

namespace TaloGameServices
Expand All @@ -19,24 +20,135 @@ public void SetProp(string key, string value)
{
props = props.Select((prop) =>
{
if (prop.key == key) prop.value = value;
if (prop.key == key)
{
prop.value = value;
}

return prop;
}).ToArray();
}
else
{
var propList = props.ToList();
propList.Add(new Prop((key, value)));
props = propList.ToArray();
props = props.Append(new Prop((key, value))).ToArray();
}
}

public void DeleteProp(string key)
{
Prop prop = props.FirstOrDefault((prop) => prop.key == key);
if (prop == null) throw new Exception($"Prop with key {key} does not exist");
var prop = props.FirstOrDefault((prop) => prop.key == key) ??
throw new Exception($"Prop with key {key} does not exist");

prop.value = null;
}

private string ToArrayKey(string key)
{
if (key.EndsWith("[]"))
{
return key;
}

return $"{key}[]";
}

public IReadOnlyList<string> GetPropArray(string key)
{
var items = props
.Where((prop) => prop.key == ToArrayKey(key) && prop.value != null)
.Select((prop) => prop.value);

return items.ToList().AsReadOnly();
}

private void EnsurePropArraySentinelRemoved(string key)
{
var arrayKey = ToArrayKey(key);

var hasSentinel = props.Any((prop) => prop.key == arrayKey && prop.value == null);
if (hasSentinel)
{
props = props.Where((prop) => prop.key != arrayKey).ToArray();
}
}

public void SetPropArray(string key, IEnumerable<string> values)
{
var validValues = values.Where((value) => !string.IsNullOrEmpty(value)).Distinct().ToList();

if (validValues.Count == 0)
{
throw new Exception($"Values for prop array {key} must contain at least one non-empty value");
}

var arrayKey = ToArrayKey(key);
props = props.Where((prop) => prop.key != arrayKey).ToArray();

props = props.Concat(validValues.Select((value) => new Prop((arrayKey, value)))).ToArray();
}

public void DeletePropArray(string key)
{
var arrayKey = ToArrayKey(key);

if (!props.Any((prop) => prop.key == arrayKey))
{
throw new Exception($"Prop array with key {key} does not exist");
}

props = props
.Where((prop) => prop.key != arrayKey)
// set a single value to null - this ensures the array is cleared
.Append(new Prop((arrayKey, null)))
.ToArray();
}

public void InsertIntoPropArray(string key, string value)
{
if (string.IsNullOrEmpty(value))
{
throw new Exception($"Value for prop array {key} cannot be null or empty");
}

var arrayKey = ToArrayKey(key);

var hasDupe = props.Any((prop) => prop.key == arrayKey && prop.value == value);
if (!hasDupe)
{
EnsurePropArraySentinelRemoved(key);
props = props.Append(new Prop((arrayKey, value))).ToArray();
}
}

private void EnsurePropArrayHasSentinel(string key)
{
var hasItems = props
.Where((prop) => prop.key == ToArrayKey(key))
.Any();

if (!hasItems)
{
props = props.Append(new Prop((ToArrayKey(key), null))).ToArray();
}
}

public void RemoveFromPropArray(string key, string value)
{
var arrayKey = ToArrayKey(key);
EnsurePropArraySentinelRemoved(key);

if (!props.Any((prop) => prop.key == arrayKey && prop.value == value))
{
EnsurePropArrayHasSentinel(key);
throw new Exception($"Value {value} does not exist in prop array {key}");
}

props = props.Where((prop) => {
var isMatchingValue = prop.key == arrayKey && prop.value == value;
return !isMatchingValue;
}).ToArray();

EnsurePropArrayHasSentinel(key);
}
}
}
}
41 changes: 41 additions & 0 deletions Assets/Talo Game Services/Talo/Runtime/Entities/Player.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using UnityEngine;
using System.Linq;
using System;
using System.Collections.Generic;

namespace TaloGameServices
{
Expand Down Expand Up @@ -37,6 +38,46 @@ public void DeleteProp(string key, bool update = true)
}
}

public void SetPropArray(string key, IEnumerable<string> values, bool update = true)
{
base.SetPropArray(key, values);

if (update)
{
Talo.Players.DebounceUpdate();
}
}

public void DeletePropArray(string key, bool update = true)
{
base.DeletePropArray(key);

if (update)
{
Talo.Players.DebounceUpdate();
}
}

public void InsertIntoPropArray(string key, string value, bool update = true)
{
base.InsertIntoPropArray(key, value);

if (update)
{
Talo.Players.DebounceUpdate();
}
}

public void RemoveFromPropArray(string key, string value, bool update = true)
{
base.RemoveFromPropArray(key, value);

if (update)
{
Talo.Players.DebounceUpdate();
}
}

public bool IsInGroupID(string groupId)
{
return groups.Any((group) => group.id == groupId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public enum SocketErrorCode {

public class SocketException : Exception
{
private SocketError errorData;
private readonly SocketError errorData;

public string Req => errorData?.req ?? "unknown";
public SocketErrorCode ErrorCode => GetErrorCode();
Expand Down
8 changes: 8 additions & 0 deletions Assets/Talo Game Services/Talo/Tests/EntityWithProps.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
using System;
using System.Collections;
using System.Linq;
using NUnit.Framework;
using UnityEngine.TestTools;

namespace TaloGameServices.Test {
internal class DeletePropArrayTest
{
[UnityTest]
public IEnumerator DeletePropArray_ArrayIsEmptyAndSentinelNullExistsInProps()
{
var player = new Player
{
props = new[] {
new Prop(("colours[]", "red")),
new Prop(("colours[]", "blue"))
}
};

player.DeletePropArray("colours", false);

Assert.AreEqual(0, player.GetPropArray("colours").Count);
// ensure the sentinel null value is in there
Assert.IsTrue(player.props.Any((prop) => prop.key == "colours[]" && prop.value == null));

yield return null;
}

[UnityTest]
public IEnumerator DeletePropArray_WhenTheArrayDoesNotExist_ThrowsAnError()
{
var player = new Player
{
props = new[] {
new Prop(("colours[]", "red")),
new Prop(("colours[]", "blue"))
}
};

try
{
player.DeletePropArray("sizes", false);
Assert.Fail("Expected exception was not thrown");
}
catch (Exception ex)
{
Assert.AreEqual(ex.Message, $"Prop array with key sizes does not exist");
}

yield return null;
}

[UnityTest]
public IEnumerator DeletePropArray_AcceptsKeyWithOrWithoutBrackets()
{
var player = new Player
{
props = new[] {
new Prop(("colours[]", "red")),
new Prop(("colours[]", "blue"))
}
};

player.DeletePropArray("colours[]", false);

Assert.AreEqual(0, player.GetPropArray("colours").Count);
// ensure the sentinel null value is in there
Assert.IsTrue(player.props.Any((prop) => prop.key == "colours[]" && prop.value == null));

yield return null;
}
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
using System;
using System.Collections;
using NUnit.Framework;
using UnityEngine.TestTools;

namespace TaloGameServices.Test {
internal class DeleteProp
{
[UnityTest]
public IEnumerator DeleteProp_WhenThePropExists_SetsTheValueToNull()
{
var player = new Player
{
props = new[] {
new Prop(("key1", "value1")),
new Prop(("key2", "value2"))
}
};

player.DeleteProp("key2", false);

Assert.IsNull(player.GetProp("key2"));

yield return null;
}

[UnityTest]
public IEnumerator DeleteProp_WhenThePropDoesNotExist_ThrowsAnError()
{
var player = new Player
{
props = new[] {
new Prop(("key1", "value1")),
new Prop(("key2", "value2"))
}
};

try
{
player.DeleteProp("key3", false);
Assert.Fail("Expected exception was not thrown");
}
catch (Exception ex)
{
Assert.AreEqual(ex.Message, $"Prop with key key3 does not exist");
}

yield return null;
}

}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading