Skip to content

Commit

Permalink
sdk callbacks
Browse files Browse the repository at this point in the history
  • Loading branch information
lcodes committed Sep 29, 2024
1 parent c3626ef commit 7e57315
Show file tree
Hide file tree
Showing 13 changed files with 411 additions and 421 deletions.
32 changes: 17 additions & 15 deletions examples~/quickstart/client/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ void User_OnUpdate(EventContext ctx, User oldValue, User newValue)

void PrintMessage(Message message)
{
var sender = User.FindByIdentity(message.Sender);
var sender = conn.RemoteTables.User.FindByIdentity(message.Sender);
var senderName = "unknown";
if (sender != null)
{
Expand All @@ -98,25 +98,27 @@ void PrintMessage(Message message)

void Message_OnInsert(EventContext ctx, Message insertedValue)
{
if (ctx != null)
if (ctx.Reducer is not Event<Reducer>.SubscribeApplied)
{
PrintMessage(insertedValue);
}
}

void Reducer_OnSetNameEvent(EventContext reducerEvent, string name)
{
if (reducerEvent.Identity == local_identity && reducerEvent.Status is UpdateStatus.Failed)
{
Console.Write($"Failed to change name to {name}");
void Reducer_OnSetNameEvent(EventContext ctx, string name) {
if (ctx.Reducer is Event<Reducer>.Reducer reducer) {
var e = reducer.ReducerEvent;
if (e.CallerIdentity == local_identity && e.Status is Status.Failed(var error)) {
Console.Write($"Failed to change name to {name}: {error}");
}
}
}

void Reducer_OnSendMessageEvent(EventContext reducerEvent, string text)
{
if (reducerEvent.Identity == local_identity && reducerEvent.Status is UpdateStatus.Failed)
{
Console.Write($"Failed to send message {text}");
void Reducer_OnSendMessageEvent(EventContext ctx, string text) {
if (ctx.Reducer is Event<Reducer>.Reducer reducer) {
var e = reducer.ReducerEvent;
if (e.CallerIdentity == local_identity && e.Status is Status.Failed(var error)) {
Console.Write($"Failed to send message {text}: {error}");
}
}
}

Expand All @@ -138,7 +140,7 @@ void OnDisconnect(DbConnection conn, WebSocketCloseStatus? status, WebSocketErro

void PrintMessagesInOrder()
{
foreach (Message message in Message.Iter().OrderBy(item => item.Sent))
foreach (Message message in conn.RemoteTables.Message.Iter().OrderBy(item => item.Sent))
{
PrintMessage(message);
}
Expand All @@ -150,9 +152,9 @@ void OnSubscriptionApplied()
PrintMessagesInOrder();
}

void onUnhandledReducerError(EventContext reducerEvent)
void onUnhandledReducerError(ReducerEvent<Reducer> reducerEvent)
{
Console.WriteLine($"Unhandled reducer error in {reducerEvent.ReducerName}: {reducerEvent.ErrMessage}");
Console.WriteLine($"Unhandled reducer error in {reducerEvent.Reducer}: {reducerEvent.Status}");
}

void ProcessThread()
Expand Down
19 changes: 1 addition & 18 deletions examples~/quickstart/client/module_bindings/Message.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ namespace SpacetimeDB.Types
{
[SpacetimeDB.Type]
[DataContract]
public partial class Message : SpacetimeDB.DatabaseTable<Message, SpacetimeDB.Types.EventContext>
public partial class Message : IDatabaseRow
{
[DataMember(Name = "sender")]
public SpacetimeDB.Identity Sender;
Expand All @@ -39,22 +39,5 @@ public Message()
this.Sender = new();
this.Text = "";
}

public static IEnumerable<Message> FilterBySender(SpacetimeDB.Identity value)
{
return Query(x => x.Sender == value);
}

public static IEnumerable<Message> FilterBySent(ulong value)
{
return Query(x => x.Sent == value);
}

public static IEnumerable<Message> FilterByText(string value)
{
return Query(x => x.Text == value);
}


}
}
36 changes: 1 addition & 35 deletions examples~/quickstart/client/module_bindings/User.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ namespace SpacetimeDB.Types
{
[SpacetimeDB.Type]
[DataContract]
public partial class User : SpacetimeDB.DatabaseTableWithPrimaryKey<User, SpacetimeDB.Types.EventContext>
public partial class User : IDatabaseRow
{
[DataMember(Name = "identity")]
public SpacetimeDB.Identity Identity;
Expand All @@ -38,39 +38,5 @@ public User()
{
this.Identity = new();
}

private static Dictionary<SpacetimeDB.Identity, User> Identity_Index = new(16);

public override void InternalOnValueInserted()
{
Identity_Index[Identity] = this;
}

public override void InternalOnValueDeleted()
{
Identity_Index.Remove(Identity);
}

public static User? FindByIdentity(SpacetimeDB.Identity value)
{
Identity_Index.TryGetValue(value, out var r);
return r;
}

public static IEnumerable<User> FilterByIdentity(SpacetimeDB.Identity value)
{
if (FindByIdentity(value) is {} found)
{
yield return found;
}
}

public static IEnumerable<User> FilterByOnline(bool value)
{
return Query(x => x.Online == value);
}

public override object GetPrimaryKeyValue() => Identity;

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
#nullable enable

using System;
using SpacetimeDB;
using System.Collections.Generic;

using SpacetimeDB.ClientApi;

namespace SpacetimeDB.Types
Expand All @@ -17,8 +18,52 @@ public interface IReducerArgs : IReducerArgsBase

public sealed class RemoteTables
{
public readonly RemoteTableHandle<EventContext, Message> Message = new();
public readonly RemoteTableHandle<EventContext, User> User = new();
public class MessageHandle : RemoteTableHandle<EventContext, Message> {
public IEnumerable<Message> FilterBySender(SpacetimeDB.Identity value) {
return Query(x => x.Sender == value);
}

public IEnumerable<Message> FilterBySent(ulong value) {
return Query(x => x.Sent == value);
}

public IEnumerable<Message> FilterByText(string value) {
return Query(x => x.Text == value);
}
}

public class UserHandle : RemoteTableHandle<EventContext, User> {
public override object? GetPrimaryKey(IDatabaseRow row) => ((User)row).Identity;

private Dictionary<SpacetimeDB.Identity, User> Identity_Index = new(16);

public override void InternalInvokeValueInserted(IDatabaseRow row) {
var value = (User)row;
Identity_Index[value.Identity] = value;
}

public override void InternalInvokeValueDeleted(IDatabaseRow row) {
Identity_Index.Remove(((User)row).Identity);
}

public User? FindByIdentity(SpacetimeDB.Identity value) {
Identity_Index.TryGetValue(value, out var r);
return r;
}

public IEnumerable<User> FilterByIdentity(SpacetimeDB.Identity value) {
if (FindByIdentity(value) is { } found) {
yield return found;
}
}

public IEnumerable<User> FilterByOnline(bool value) {
return Query(x => x.Online == value);
}
}

public readonly MessageHandle Message = new();
public readonly UserHandle User = new();
}

public sealed class RemoteReducers : RemoteBase<DbConnection>
Expand Down Expand Up @@ -61,18 +106,25 @@ public bool InvokeSetName(EventContext ctx, SetNameArgsStruct args)
}
}

public partial class EventContext : EventContextBase<RemoteTables, RemoteReducers>
{
public IReducerArgs? Args { get; }

public string ReducerName => Args?.ReducerName ?? "<none>";
public partial record EventContext : DbContext<RemoteTables>, IEventContext {
public readonly RemoteReducers Reducers;
public readonly Event<Reducer> Reducer;

public EventContext(DbConnection conn, TransactionUpdate update, IReducerArgs? args) : base(conn.RemoteTables, conn.RemoteReducers, update) => Args = args;

public override bool InvokeHandler() => Args?.InvokeHandler(this) ?? false;
internal EventContext(DbConnection conn, Event<Reducer> reducer) : base(conn.RemoteTables) {
Reducers = conn.RemoteReducers;
Reducer = reducer;
}
}

public class DbConnection : DbConnectionBase<DbConnection, EventContext>
[Type]
public partial record Reducer : TaggedEnum<(
SendMessageArgsStruct SendMessage,
SetNameArgsStruct SetName,
Unit IdentityConnected,
Unit IdentityDisconnected
)>;

public class DbConnection : DbConnectionBase<DbConnection, Reducer>
{
public readonly RemoteTables RemoteTables = new();
public readonly RemoteReducers RemoteReducers;
Expand All @@ -81,23 +133,35 @@ public DbConnection()
{
RemoteReducers = new(this);

clientDB.AddTable<Message>();
clientDB.AddTable<User>();
clientDB.AddTable<Message>("Message", RemoteTables.Message);
clientDB.AddTable<User>("User", RemoteTables.User);
}

protected override EventContext ReducerEventFromDbEvent(TransactionUpdate update)
protected override Reducer ToReducer(TransactionUpdate update)
{
var encodedArgs = update.ReducerCall.Args;
IReducerArgs? args = update.ReducerCall.ReducerName switch {
"send_message" => BSATNHelpers.Decode<SendMessageArgsStruct>(encodedArgs),
"set_name" => BSATNHelpers.Decode<SetNameArgsStruct>(encodedArgs),
"<none>" => null,
"__identity_connected__" => null,
"__identity_disconnected__" => null,
"" => null,
return update.ReducerCall.ReducerName switch {
"send_message" => new Reducer.SendMessage(BSATNHelpers.Decode<SendMessageArgsStruct>(encodedArgs)),
"set_name" => new Reducer.SetName(BSATNHelpers.Decode<SetNameArgsStruct>(encodedArgs)),
"__identity_connected__" => new Reducer.IdentityConnected(default),
"__identity_disconnected__" => new Reducer.IdentityDisconnected(default),
var reducer => throw new ArgumentOutOfRangeException("Reducer", $"Unknown reducer {reducer}")
};
return new EventContext(this, update, args);
}

protected override IEventContext ToEventContext(Event<Reducer> reducerEvent) {
return new EventContext(this, reducerEvent);
}

protected override bool Dispatch(IEventContext context, Reducer reducer) {
var eventContext = (EventContext)context;
return reducer switch {
Reducer.SendMessage(var args) => RemoteReducers.InvokeSendMessage(eventContext, args),
Reducer.SetName(var args) => RemoteReducers.InvokeSetName(eventContext, args),
Reducer.IdentityConnected => true,
Reducer.IdentityDisconnected => true,
_ => throw new ArgumentOutOfRangeException("Reducer", $"Unknown reducer {reducer}")
};
}
}
}
18 changes: 9 additions & 9 deletions examples~/quickstart/server/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,27 +1,27 @@
use spacetimedb::{spacetimedb, ReducerContext, Identity, Timestamp};
use spacetimedb::{ReducerContext, Identity, Timestamp};
use anyhow::{Result, anyhow};

#[spacetimedb(table(public))]
#[spacetimedb::table(name = User, public)]
pub struct User {
#[primarykey]
#[primary_key]
identity: Identity,
name: Option<String>,
online: bool,
}

#[spacetimedb(table(public))]
#[spacetimedb::table(name = Message, public)]
pub struct Message {
sender: Identity,
sent: Timestamp,
text: String,
}

#[spacetimedb(init)]
#[spacetimedb::reducer(init)]
pub fn init() {
// Called when the module is initially published
}

#[spacetimedb(connect)]
#[spacetimedb::reducer(client_connected)]
pub fn identity_connected(ctx: ReducerContext) {
if let Some(user) = User::filter_by_identity(&ctx.sender) {
// If this is a returning user, i.e. we already have a `User` with this `Identity`,
Expand All @@ -38,7 +38,7 @@ pub fn identity_connected(ctx: ReducerContext) {
}
}

#[spacetimedb(disconnect)]
#[spacetimedb::reducer(client_disconnected)]
pub fn identity_disconnected(ctx: ReducerContext) {
if let Some(user) = User::filter_by_identity(&ctx.sender) {
User::update_by_identity(&ctx.sender, User { online: false, ..user });
Expand All @@ -57,7 +57,7 @@ fn validate_name(name: String) -> Result<String> {
}
}

#[spacetimedb(reducer)]
#[spacetimedb::reducer]
pub fn set_name(ctx: ReducerContext, name: String) -> Result<()> {
let name = validate_name(name)?;
if let Some(user) = User::filter_by_identity(&ctx.sender) {
Expand All @@ -76,7 +76,7 @@ fn validate_message(text: String) -> Result<String> {
}
}

#[spacetimedb(reducer)]
#[spacetimedb::reducer]
pub fn send_message(ctx: ReducerContext, text: String) -> Result<()> {
// Things to consider:
// - Rate-limit messages per-user.
Expand Down
Loading

0 comments on commit 7e57315

Please sign in to comment.