Skip to content

Instantly share code, notes, and snippets.

@dylanh724
Last active March 4, 2024 03:32
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dylanh724/68067b4e843ea6e99fbd297fe1a87c49 to your computer and use it in GitHub Desktop.
Save dylanh724/68067b4e843ea6e99fbd297fe1a87c49 to your computer and use it in GitHub Desktop.
SpacetimeDB | Docs | Unity Pt1 | Rust Parity: lib.cs
// using SpacetimeDB; // Useful if I wanted to exclude `SpacetimeDB` attribute prefixes
using SpacetimeDB.Module;
using static SpacetimeDB.Runtime;
/// Code From the Unity Pt1 Basic Tutorial: https://spacetimedb.com/docs/unity/part-1
static partial class Module
{
[SpacetimeDB.Reducer(ReducerKind.Update)]
public static void OnConnect(DbEventArgs dbEventArgs)
{
Log("User connected");
}
/// We're using this table as a singleton,
/// so there should typically only be one element where the version is 0.
[SpacetimeDB.Table]
public partial class Config
{
[SpacetimeDB.Column(ColumnAttrs.PrimaryKey)]
public int Version;
public string? MessageOfTheDay;
}
/// This allows us to store 3D points in tables.
[SpacetimeDB.Type]
public partial class StdbVector3
{
public float X;
public float Y;
public float Z;
}
/// This stores information related to all entities in our game. In this tutorial
/// all entities must at least have an entity_id, a position, a direction and they
/// must specify whether or not they are moving.
[SpacetimeDB.Table]
public partial class EntityComponent
{
[SpacetimeDB.Column(ColumnAttrs.PrimaryKeyAuto)]
public ulong EntityId;
public StdbVector3? Position;
public float Direction;
public bool Moving;
}
/// All players have this component and it associates an entity with the user's
/// Identity. It also stores their username and whether or not they're logged in.
[SpacetimeDB.Table]
public partial class PlayerComponent
{
// An EntityId that matches an EntityId in the `EntityComponent` table.
[SpacetimeDB.Column(ColumnAttrs.PrimaryKey)]
public ulong EntityId;
// The user's identity, which is unique to each player
[SpacetimeDB.Column(ColumnAttrs.Unique)]
public Identity Identity;
public string? Username;
public bool LoggedIn;
}
/// This reducer is called when the user logs in for the first time and
/// enters a username.
[SpacetimeDB.Reducer]
public static void CreatePlayer(DbEventArgs dbEvent, string username)
{
// Get the Identity of the client who called this reducer
Identity sender = dbEvent.Sender;
// Make sure we don't already have a player with this identity
PlayerComponent? player = PlayerComponent.FindByIdentity(sender);
if (player is not null)
{
throw new ArgumentException("Player already exists");
}
// Create a new entity for this player
try
{
new EntityComponent
{
EntityId = 0, // TODO: is 0 redundantly the same as leaving null due to `PrimaryKeyAuto`?
Position = new StdbVector3 { X = 0, Y = 0, Z = 0 },
Direction = 0,
Moving = false,
}.Insert();
}
catch
{
Log("Error: Failed to create a unique PlayerComponent", LogLevel.Error);
throw;
}
// The PlayerComponent uses the same entity_id and stores the identity of
// the owner, username, and whether or not they are logged in.
try
{
new PlayerComponent
{
// EntityId = 0, // 0 is the same as leaving null to get a new, unique Id
Identity = dbEvent.Sender,
Username = username,
LoggedIn = true,
}.Insert();
}
catch
{
Log("Error: Failed to insert PlayerComponent", LogLevel.Error);
throw;
}
Log($"Player created: {username}");
}
/// Called when the module is initially published
[SpacetimeDB.Reducer(ReducerKind.Init)]
public static void OnInit()
{
try
{
new Config
{
Version = 0,
MessageOfTheDay = "Hello, World!",
}.Insert();
}
catch
{
Log("Error: Failed to insert Config", LogLevel.Error);
throw;
}
}
/// Called when the client connects, we update the LoggedIn state to true
[SpacetimeDB.Reducer(ReducerKind.Init)]
public static void ClientConnected(DbEventArgs dbEvent) =>
UpdatePlayerLoginState(dbEvent, loggedIn:true);
/// Called when the client disconnects, we update the logged_in state to false
[SpacetimeDB.Reducer(ReducerKind.Disconnect)]
public static void ClientDisonnected(DbEventArgs dbEvent) =>
UpdatePlayerLoginState(dbEvent, loggedIn:false);
/// This helper function gets the PlayerComponent, sets the LoggedIn
/// variable and updates the PlayerComponent table row.
private static void UpdatePlayerLoginState(DbEventArgs dbEvent, bool loggedIn)
{
PlayerComponent? player = PlayerComponent.FindByIdentity(dbEvent.Sender);
if (player is null)
{
throw new ArgumentException("Player not found");
}
player.LoggedIn = loggedIn;
PlayerComponent.UpdateByIdentity(dbEvent.Sender, player);
}
/// Updates the position of a player. This is also called when the player stops moving.
[SpacetimeDB.Reducer]
private static void UpdatePlayerPosition(
DbEventArgs dbEvent,
StdbVector3 position,
float direction,
bool moving)
{
// First, look up the player using the sender identity
PlayerComponent? player = PlayerComponent.FindByIdentity(dbEvent.Sender);
if (player is null)
{
throw new ArgumentException("Player not found");
}
// Use the Player's EntityId to retrieve and update the EntityComponent
ulong playerEntityId = player.EntityId;
EntityComponent? entity = EntityComponent.FindByEntityId(playerEntityId);
if (entity is null)
{
throw new ArgumentException($"Player Entity '{playerEntityId}' not found");
}
entity.Position = position;
entity.Direction = direction;
entity.Moving = moving;
EntityComponent.UpdateByEntityId(playerEntityId, entity);
}
[SpacetimeDB.Table]
public partial class ChatMessage
{
// The primary key for this table will be auto-incremented
[SpacetimeDB.Column(ColumnAttrs.PrimaryKeyAuto)]
// The entity id of the player that sent the message
public ulong SenderId;
// Message contents
public string? Text;
}
/// Adds a chat entry to the ChatMessage table
[SpacetimeDB.Reducer]
public static void SendChatMessage(DbEventArgs dbEvent, string text)
{
// Get the player's entity id
PlayerComponent? player = PlayerComponent.FindByIdentity(dbEvent.Sender);
if (player is null)
{
throw new ArgumentException("Player not found");
}
// Insert the chat message
new ChatMessage
{
SenderId = player.EntityId,
Text = text,
}.Insert();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment