Skip to content

Commit

Permalink
Test with upstream subscription API + fix respawns + add suicide for …
Browse files Browse the repository at this point in the history
…testing (#13)

* Test with upstream subscription API + fix respawns + add suicide for testing

* Add comment on not loading Auth Token for testing.

* Remove dangling meta file?

* Switch server-csharp to local build temporarily & fix all issues

* Remove needless comment

* Add comment to csproj

* Remove debug logs
  • Loading branch information
kazimuth authored Jan 23, 2025
1 parent 5a50d5d commit f9e4513
Show file tree
Hide file tree
Showing 18 changed files with 314 additions and 185 deletions.
8 changes: 0 additions & 8 deletions client/Assets/Scenes/Main.meta

This file was deleted.

1 change: 1 addition & 0 deletions client/Assets/Scenes/Main.unity
Original file line number Diff line number Diff line change
Expand Up @@ -501,6 +501,7 @@ MonoBehaviour:
borderThickness: 2
borderMaterial: {fileID: 2100000, guid: eb000c9de35d73e4d81c6a0831a9e7bc, type: 2}
starBackgroundPrefab: {fileID: 0}
deathScreen: {fileID: 369182684}
--- !u!4 &207278860
Transform:
m_ObjectHideFlags: 0
Expand Down
7 changes: 0 additions & 7 deletions client/Assets/Scripts/DeathScreen.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,8 @@

public class DeathScreen : MonoBehaviour
{
public static DeathScreen Instance { get; private set; }

public Button RespawnButton;

public void Awake()
{
Instance = this;
}

public void SetVisible(bool visible)
{
gameObject.SetActive(visible);
Expand Down
153 changes: 80 additions & 73 deletions client/Assets/Scripts/GameManager.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using SpacetimeDB;
using SpacetimeDB.Types;
using UnityEngine;
Expand All @@ -17,13 +18,14 @@ public class GameManager : MonoBehaviour
public float borderThickness = 2;
public Material borderMaterial;
public ParallaxBackground starBackgroundPrefab;
public DeathScreen deathScreen;

public static GameManager Instance { get; private set; }
public static GameManager Instance { get; private set; }
public static Identity LocalIdentity { get; private set; }
public static DbConnection Conn { get; private set; }

public static Dictionary<uint, EntityController> Entities = new Dictionary<uint, EntityController>();
public static Dictionary<uint, PlayerController> Players = new Dictionary<uint, PlayerController>();
public static Dictionary<uint, PlayerController> Players = new Dictionary<uint, PlayerController>();

private void Start()
{
Expand All @@ -41,20 +43,25 @@ private void Start()

// If the user has a SpacetimeDB auth token stored in the Unity PlayerPrefs,
// we can use it to authenticate the connection.
if (PlayerPrefs.HasKey(AuthToken.GetTokenKey()))
// For testing purposes, it is often convenient to comment the following lines out and
// export an executable for the project using File -> Build Settings.
// Then, you can run the executable multiple times. Since the executable will not check for
// a saved auth token, each run of will receive a different Identifier,
// and their circles will be able to eat each other.
if (PlayerPrefs.HasKey(AuthToken.GetTokenKey()))
{
builder = builder.WithCredentials((default, AuthToken.Token));
builder = builder.WithToken(AuthToken.Token);
}

// Building the connection will establish a connection to the SpacetimeDB
// server.
Conn = builder.Build();

/* BEGIN: not in tutorial */
/* BEGIN: not in tutorial */
#pragma warning disable CS0612 // Type or member is obsolete
Conn.onUnhandledReducerError += InstanceOnUnhandledReducerError;
Conn.onUnhandledReducerError += InstanceOnUnhandledReducerError;
#pragma warning restore CS0612 // Type or member is obsolete
/* END: not in tutorial */
/* END: not in tutorial */
}

// Called when we connect to SpacetimeDB and receive our client identity
Expand All @@ -64,19 +71,19 @@ void HandleConnect(DbConnection conn, Identity identity, string token)
AuthToken.SaveToken(token);
LocalIdentity = identity;

conn.Db.Circle.OnInsert += CircleOnInsert;
conn.Db.Entity.OnUpdate += EntityOnUpdate;
conn.Db.Entity.OnDelete += EntityOnDelete;
conn.Db.Food.OnInsert += FoodOnInsert;
conn.Db.Player.OnInsert += PlayerOnInsert;
conn.Db.Player.OnDelete += PlayerOnDelete;
conn.Db.Circle.OnInsert += CircleOnInsert;
conn.Db.Entity.OnUpdate += EntityOnUpdate;
conn.Db.Entity.OnDelete += EntityOnDelete;
conn.Db.Food.OnInsert += FoodOnInsert;
conn.Db.Player.OnInsert += PlayerOnInsert;
conn.Db.Player.OnDelete += PlayerOnDelete;

OnConnected?.Invoke();

// Request all tables
Conn.SubscriptionBuilder()
.OnApplied(HandleSubscriptionApplied)
.Subscribe("SELECT * FROM *");
.SubscribeToAllTables();
}

void HandleConnectError(Exception ex)
Expand Down Expand Up @@ -125,68 +132,68 @@ private void SetupArena(float worldSize)
}

private void CreateBorderCube(Vector2 position, Vector2 scale)
{
var cube = GameObject.CreatePrimitive(PrimitiveType.Cube);
{
var cube = GameObject.CreatePrimitive(PrimitiveType.Cube);
cube.name = "Border";
cube.transform.localScale = new Vector3(scale.x, scale.y, 1);
cube.transform.position = new Vector3(position.x, position.y, 1);
cube.GetComponent<MeshRenderer>().material = borderMaterial;
}
cube.transform.localScale = new Vector3(scale.x, scale.y, 1);
cube.transform.position = new Vector3(position.x, position.y, 1);
cube.GetComponent<MeshRenderer>().material = borderMaterial;
}

private static void CircleOnInsert(EventContext context, Circle insertedValue)
{
var player = GetOrCreatePlayer(insertedValue.PlayerId);
var entityController = PrefabManager.SpawnCircle(insertedValue, player);
Entities.Add(insertedValue.EntityId, entityController);
}

private static void EntityOnUpdate(EventContext context, Entity oldEntity, Entity newEntity)
{
if (!Entities.TryGetValue(newEntity.EntityId, out var entityController))
{
return;
}
entityController.OnEntityUpdated(newEntity);
}

private static void EntityOnDelete(EventContext context, Entity oldEntity)
{
if (Entities.Remove(oldEntity.EntityId, out var entityController))
{
entityController.OnDelete(context);
}
}

private static void FoodOnInsert(EventContext context, Food insertedValue)
{
var entityController = PrefabManager.SpawnFood(insertedValue);
Entities.Add(insertedValue.EntityId, entityController);
}

private static void PlayerOnInsert(EventContext context, Player insertedPlayer)
{
GetOrCreatePlayer(insertedPlayer.PlayerId);
}

private static void PlayerOnDelete(EventContext context, Player deletedvalue)
{
if (Players.Remove(deletedvalue.PlayerId, out var playerController))
{
GameObject.Destroy(playerController.gameObject);
}
}

private static PlayerController GetOrCreatePlayer(uint playerId)
{
if (!Players.TryGetValue(playerId, out var playerController))
{
var player = Conn.Db.Player.PlayerId.Find(playerId);
playerController = PrefabManager.SpawnPlayer(player);
Players.Add(playerId, playerController);
}

return playerController;
}
{
var player = GetOrCreatePlayer(insertedValue.PlayerId);
var entityController = PrefabManager.SpawnCircle(insertedValue, player);
Entities.Add(insertedValue.EntityId, entityController);
}

private static void EntityOnUpdate(EventContext context, Entity oldEntity, Entity newEntity)
{
if (!Entities.TryGetValue(newEntity.EntityId, out var entityController))
{
return;
}
entityController.OnEntityUpdated(newEntity);
}

private static void EntityOnDelete(EventContext context, Entity oldEntity)
{
if (Entities.Remove(oldEntity.EntityId, out var entityController))
{
entityController.OnDelete(context);
}
}

private static void FoodOnInsert(EventContext context, Food insertedValue)
{
var entityController = PrefabManager.SpawnFood(insertedValue);
Entities.Add(insertedValue.EntityId, entityController);
}

private static void PlayerOnInsert(EventContext context, Player insertedPlayer)
{
GetOrCreatePlayer(insertedPlayer.PlayerId);
}

private static void PlayerOnDelete(EventContext context, Player deletedvalue)
{
if (Players.Remove(deletedvalue.PlayerId, out var playerController))
{
GameObject.Destroy(playerController.gameObject);
}
}

private static PlayerController GetOrCreatePlayer(uint playerId)
{
if (!Players.TryGetValue(playerId, out var playerController))
{
var player = Conn.Db.Player.PlayerId.Find(playerId);
playerController = PrefabManager.SpawnPlayer(player);
Players.Add(playerId, playerController);
}

return playerController;
}

public static bool IsConnected()
{
Expand Down
85 changes: 45 additions & 40 deletions client/Assets/Scripts/PlayerController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,28 +6,28 @@

public class PlayerController : MonoBehaviour
{
const int SEND_UPDATES_PER_SEC = 20;
const float SEND_UPDATES_FREQUENCY = 1f / SEND_UPDATES_PER_SEC;
const int SEND_UPDATES_PER_SEC = 20;
const float SEND_UPDATES_FREQUENCY = 1f / SEND_UPDATES_PER_SEC;

public static PlayerController Local { get; private set; }

private uint PlayerId;
private uint PlayerId;
private float LastMovementSendTimestamp;
private Vector2? LockInputPosition;
private List<CircleController> OwnedCircles = new List<CircleController>();
private List<CircleController> OwnedCircles = new List<CircleController>();

public string Username => GameManager.Conn.Db.Player.PlayerId.Find(PlayerId).Name;
public int NumberOfOwnedCircles => OwnedCircles.Count;
public bool IsLocalPlayer => this == Local;
public string Username => GameManager.Conn.Db.Player.PlayerId.Find(PlayerId).Name;
public int NumberOfOwnedCircles => OwnedCircles.Count;
public bool IsLocalPlayer => this == Local;

public void Initialize(Player player)
public void Initialize(Player player)
{
PlayerId = player.PlayerId;
if (player.Identity == GameManager.LocalIdentity)
{
Local = this;
}
}
}

private void OnDestroy()
{
Expand All @@ -48,28 +48,28 @@ public void OnCircleSpawned(CircleController circle)
}

public void OnCircleDeleted(CircleController deletedCircle)
{
// This means we got eaten
if (OwnedCircles.Remove(deletedCircle) && IsLocalPlayer && OwnedCircles.Count == 0)
{
DeathScreen.Instance.SetVisible(true);
}
}

public uint TotalMass()
{
// This means we got eaten
if (OwnedCircles.Remove(deletedCircle) && IsLocalPlayer && OwnedCircles.Count == 0)
{
GameManager.Instance.deathScreen.SetVisible(true);
}
}

public uint TotalMass()
{
return (uint)OwnedCircles
.Select(circle => GameManager.Conn.Db.Entity.EntityId.Find(circle.EntityId))
.Sum(e => e?.Mass ?? 0); //If this entity is being deleted on the same frame that we're moving, we can have a null entity here.
}
.Sum(e => e?.Mass ?? 0); //If this entity is being deleted on the same frame that we're moving, we can have a null entity here.
}

public Vector2? CenterOfMass()
{
if (OwnedCircles.Count == 0)
{
return null;
}

Vector2 totalPos = Vector2.zero;
float totalMass = 0;
foreach (var circle in OwnedCircles)
Expand All @@ -81,9 +81,9 @@ public uint TotalMass()
}

return totalPos / totalMass;
}
}

public void Update()
public void Update()
{
if (!IsLocalPlayer || NumberOfOwnedCircles == 0)
{
Expand All @@ -100,13 +100,18 @@ public void Update()
if (LockInputPosition.HasValue)
{
LockInputPosition = null;
}
}
else
{
LockInputPosition = (Vector2)Input.mousePosition;
LockInputPosition = (Vector2)Input.mousePosition;
}
}

if (Input.GetKeyDown(KeyCode.S))
{
GameManager.Conn.Reducers.Suicide();
}

// Throttled input requests
if (Time.time - LastMovementSendTimestamp >= SEND_UPDATES_FREQUENCY)
{
Expand All @@ -120,26 +125,26 @@ public void Update()
};
var centerOfScreen = screenSize / 2;

var direction = (mousePosition - centerOfScreen) / (screenSize.y / 3);
var direction = (mousePosition - centerOfScreen) / (screenSize.y / 3);
if (testInputEnabled) { direction = testInput; }
GameManager.Conn.Reducers.UpdatePlayerInput(direction);
}
}
}

private void OnGUI()
{
if (!IsLocalPlayer || !GameManager.IsConnected())
{
return;
}
private void OnGUI()
{
if (!IsLocalPlayer || !GameManager.IsConnected())
{
return;
}

GUI.Label(new Rect(0, 0, 100, 50), $"Total Mass: {TotalMass()}");
}
GUI.Label(new Rect(0, 0, 100, 50), $"Total Mass: {TotalMass()}");
}

//Automated testing members
private bool testInputEnabled;
private Vector2 testInput;
//Automated testing members
private bool testInputEnabled;
private Vector2 testInput;

public void SetTestInput(Vector2 input) => testInput = input;
public void EnableTestInput() => testInputEnabled = true;
public void SetTestInput(Vector2 input) => testInput = input;
public void EnableTestInput() => testInputEnabled = true;
}
Loading

0 comments on commit f9e4513

Please sign in to comment.