Fixes for #1 and #2 and basic rls implemetation

This commit is contained in:
ookami125 2025-04-17 01:32:59 -04:00
parent 9456d51538
commit 4aa7fc1ff4
6 changed files with 128 additions and 72 deletions

View file

@ -1,6 +0,0 @@
#!/bin/bash
spacetime logout
spacetime login --server-issued-login local
spacetime publish -y --server local --bin-path=zig-out/bin/stdb-zig-helloworld.wasm
DB_HASH=$(spacetime list 2>/dev/null | tail -1)
spacetime logs $DB_HASH

View file

@ -9,7 +9,7 @@ if [[ "$func" == "publish" ]]; then
spacetime login --server-issued-login local
spacetime publish -y --server local --bin-path=zig-out/bin/blackholio.wasm blackholio
DB_HASH=$(spacetime list 2>/dev/null | tail -1)
spacetime logs $DB_HASH
spacetime logs $DB_HASH -n 15
exit $?
fi

View file

@ -188,6 +188,9 @@ pub const spacespec = spacetime.Spec{
.params = &.{ "_timer", },
})
},
.row_level_security = &.{
"SELECT * FROM logged_out_player WHERE identity = :sender"
}
};
pub const DbVector2 = struct {
@ -356,6 +359,7 @@ pub fn disconnect(ctx: *spacetime.ReducerContext) !void {
// Remove any circles from the arena
var iter = try ctx.db.get("circle").col("player_id").filter(.{ .player_id = player.player_id });
defer iter.close();
while (try iter.next()) |circle_val| {
try ctx.db.get("entity").col("entity_id").delete(.{ .entity_id = circle_val.entity_id, });
try ctx.db.get("circle").col("entity_id").delete(.{ .entity_id = circle_val.entity_id, });
@ -435,6 +439,7 @@ pub fn suicide(ctx: *spacetime.ReducerContext) !void {
.find(.{ .identity = ctx.sender})).?;
var circles = try ctx.db.get("circle").col("player_id").filter(.{ .player_id = player.player_id});
defer circles.close();
while(try circles.next()) |circle| {
try destroy_entity(ctx, circle.entity_id);
@ -450,6 +455,7 @@ pub fn update_player_input(ctx: *spacetime.ReducerContext, direction: DbVector2)
.col("identity")
.find(.{ .identity = ctx.sender})).?;
var circles = try ctx.db.get("circle").col("player_id").filter(.{ .player_id = player.player_id});
defer circles.close();
while(try circles.next()) |circle| {
var copy_circle = circle;
copy_circle.direction = direction.normalized();
@ -491,22 +497,26 @@ pub fn move_all_players(ctx: *spacetime.ReducerContext, _timer: MoveAllPlayersTi
.db.get("config").col("id")
.find(.{ .id = 0 })).?.world_size;
var circle_directions = std.AutoHashMap(u32, DbVector2).init(ctx.db.allocator);
var circleIter = ctx.db.get("circle").iter();
var circle_directions = std.AutoHashMap(u32, DbVector2).init(ctx.allocator);
var circleIter = try ctx.db.get("circle").iter();
defer circleIter.close();
while(try circleIter.next()) |circle| {
try circle_directions.put(circle.entity_id, circle.direction.scale(circle.speed));
}
var playerIter = ctx.db.get("player").iter();
var playerIter = try ctx.db.get("player").iter();
defer playerIter.close();
while(try playerIter.next()) |player| {
var circles = std.ArrayList(Circle).init(ctx.db.allocator);
var circles = std.ArrayList(Circle).init(ctx.allocator);
var circlesIter1 = try ctx.db.get("circle").col("player_id")
.filter(.{ .player_id = player.player_id});
defer circlesIter1.close();
while(try circlesIter1.next()) |circle| {
try circles.append(circle);
}
var player_entities = std.ArrayList(Entity).init(ctx.db.allocator);
var player_entities = std.ArrayList(Entity).init(ctx.allocator);
for(circles.items) |c| {
try player_entities.append((try ctx.db.get("entity").col("entity_id").find(.{ .entity_id = c.entity_id})).?);
}
@ -575,7 +585,8 @@ pub fn move_all_players(ctx: *spacetime.ReducerContext, _timer: MoveAllPlayersTi
}
}
var circleIter2 = ctx.db.get("circle").iter();
var circleIter2 = try ctx.db.get("circle").iter();
defer circleIter2.close();
while(try circleIter2.next()) |circle| {
const circle_entity_n = (ctx.db.get("entity").col("entity_id").find(.{ .entity_id = circle.entity_id }) catch {
continue;
@ -586,22 +597,24 @@ pub fn move_all_players(ctx: *spacetime.ReducerContext, _timer: MoveAllPlayersTi
const new_pos = circle_entity.position.add(direction.scale(mass_to_max_move_speed(circle_entity.mass)));
const min = circle_radius;
const max = @as(f32, @floatFromInt(world_size)) - circle_radius;
if(max < min) continue;
circle_entity.position.x = std.math.clamp(new_pos.x, min, max);
circle_entity.position.y = std.math.clamp(new_pos.y, min, max);
try ctx.db.get("entity").col("entity_id").update(circle_entity);
}
// Check collisions
var entities = std.AutoHashMap(u32, Entity).init(ctx.db.allocator);
var entitiesIter = ctx.db.get("entity").iter();
var entities = std.AutoHashMap(u32, Entity).init(ctx.allocator);
var entitiesIter = try ctx.db.get("entity").iter();
defer entitiesIter.close();
while(try entitiesIter.next()) |e| {
try entities.put(e.entity_id, e);
}
var circleIter3 = ctx.db.get("circle").iter();
var circleIter3 = try ctx.db.get("circle").iter();
defer circleIter3.close();
while(try circleIter3.next()) |circle| {
// let span = spacetimedb::time_span::Span::start("collisions");
var circle_entity = entities.get(circle.entity_id).?;
_ = &circle_entity;
var entityIter = entities.iterator();
while (entityIter.next()) |other_entity| {
if(other_entity.value_ptr.entity_id == circle_entity.entity_id) {
@ -670,12 +683,13 @@ pub fn player_split(ctx: *spacetime.ReducerContext) !void {
const player = (try ctx
.db.get("player").col("identity")
.find(.{ .identity = ctx.sender})).?;
var circles = std.ArrayList(Circle).init(ctx.db.allocator);
var circles = std.ArrayList(Circle).init(ctx.allocator);
var circlesIter = try ctx
.db
.get("circle")
.col("player_id")
.filter(.{ .player_id = player.player_id});
defer circlesIter.close();
while(try circlesIter.next()) |circle| {
try circles.append(circle);
}
@ -756,7 +770,8 @@ pub fn spawn_food(ctx: *spacetime.ReducerContext, _: SpawnFoodTimer) !void {
}
pub fn circle_decay(ctx: *spacetime.ReducerContext, _: CircleDecayTimer) !void {
var circleIter = ctx.db.get("circle").iter();
var circleIter = try ctx.db.get("circle").iter();
defer circleIter.close();
while(try circleIter.next()) |circle| {
var circle_entity = (try ctx
.db
@ -779,7 +794,6 @@ pub fn calculate_center_of_mass(entities: []const Entity) DbVector2 {
}
break :blk sum;
};
//entities.iter().map(|e| e.position * e.mass as f32).sum();
const center_of_mass: DbVector2 = blk: {
var sum: DbVector2 = 0;
for(entities) |entity| {
@ -792,16 +806,17 @@ pub fn calculate_center_of_mass(entities: []const Entity) DbVector2 {
}
pub fn circle_recombine(ctx: *spacetime.ReducerContext, timer: CircleRecombineTimer) !void {
var circles = std.ArrayList(Circle).init(ctx.db.allocator);
var circles = std.ArrayList(Circle).init(ctx.allocator);
var circlesIter = try ctx
.db
.get("circle")
.col("player_id")
.filter(.{ .player_id = timer.player_id });
defer circlesIter.close();
while(try circlesIter.next()) |circle| {
try circles.append(circle);
}
var recombining_entities = std.ArrayList(Entity).init(ctx.db.allocator);
var recombining_entities = std.ArrayList(Entity).init(ctx.allocator);
for(circles.items) |circle| {
if(@as(f32, @floatFromInt(ctx.timestamp.__timestamp_micros_since_unix_epoch__ - circle.last_split_time.__timestamp_micros_since_unix_epoch__)) >= SPLIT_RECOMBINE_DELAY_SEC) {
const entity = (try ctx.db

View file

@ -64,7 +64,13 @@ pub fn logFn(comptime level: std.log.Level, comptime _: @TypeOf(.enum_literal),
pub const BytesSink = extern struct { inner: u32 };
pub const BytesSource = extern struct { inner: u32 };
pub const TableId = extern struct { _inner: u32, };
pub const RowIter = extern struct { _inner: u32, pub const INVALID = RowIter{ ._inner = 0}; };
pub const RowIter = extern struct {
_inner: u32,
pub const INVALID = RowIter{ ._inner = 0};
pub fn invalid(self: @This()) bool {
return self._inner == 0;
}
};
pub const IndexId = extern struct{ _inner: u32 };
pub const ColId = extern struct { _inner: u16 };
@ -121,6 +127,7 @@ pub const SpacetimeValue = enum(u1) {
};
pub const SpacetimeError = error {
UNKNOWN,
HOST_CALL_FAILURE,
NOT_IN_TRANSACTION,
BSATN_DECODE_ERROR,
@ -586,6 +593,7 @@ pub const Table = struct {
pub const Spec = struct {
tables: []const Table,
reducers: []const SpecReducer,
row_level_security: []const []const u8,
includes: []const Spec = &.{},
};
@ -598,6 +606,8 @@ pub fn SpecBuilder(comptime spec: Spec) RawModuleDefV9 {
var raw_types: []const AlgebraicType = &[_]AlgebraicType{};
var types: []const RawTypeDefV9 = &[_]RawTypeDefV9{};
var row_level_security: []const RawRowLevelSecurityDefV9 = &[_]RawRowLevelSecurityDefV9{};
var structDecls: []const StructImpl = &[_]StructImpl{};
for(spec.tables) |table| {
@ -773,6 +783,14 @@ pub fn SpecBuilder(comptime spec: Spec) RawModuleDefV9 {
};
}
for(spec.row_level_security) |rls| {
row_level_security = row_level_security ++ &[_]RawRowLevelSecurityDefV9{
RawRowLevelSecurityDefV9{
.sql = rls,
}
};
}
return .{
.typespace = .{
.types = raw_types,
@ -781,7 +799,7 @@ pub fn SpecBuilder(comptime spec: Spec) RawModuleDefV9 {
.reducers = reducerDefs,
.types = types,
.misc_exports = &[_]RawMiscModuleExportV9{},
.row_level_security = &[_]RawRowLevelSecurityDefV9{},
.row_level_security = row_level_security,
};
}
}
@ -830,14 +848,19 @@ pub export fn __call_reducer__(
) i16 {
_ = err;
const allocator = std.heap.wasm_allocator;
const backend_allocator = std.heap.wasm_allocator;
var arena_allocator = std.heap.ArenaAllocator.init(backend_allocator);
defer arena_allocator.deinit();
const allocator = arena_allocator.allocator();
var ctx: ReducerContext = .{
.allocator = allocator,
.sender = std.mem.bytesAsValue(Identity, std.mem.sliceAsBytes(&[_]u64{ sender_0, sender_1, sender_2, sender_3})).*,
.timestamp = Timestamp{ .__timestamp_micros_since_unix_epoch__ = @intCast(timestamp), },
.connection_id = std.mem.bytesAsValue(ConnectionId, std.mem.sliceAsBytes(&[_]u64{ conn_id_0, conn_id_1})).*,
.db = .{
.allocator = allocator,
.allocator = backend_allocator,
.frame_allocator = allocator,
},
};

View file

@ -109,9 +109,8 @@ fn serialize_raw_misc_module_export_v9(array: *std.ArrayList(u8), val: RawMiscMo
}
fn serialize_raw_row_level_security_def_v9(array: *std.ArrayList(u8), val: RawRowLevelSecurityDefV9) !void {
_ = array;
_ = val;
unreachable;
try array.appendSlice(&std.mem.toBytes(@as(u32, @intCast(val.sql.len))));
try array.appendSlice(val.sql);
}
fn serialize_raw_index_algorithm(array: *std.ArrayList(u8), val: RawIndexAlgorithm) !void {

View file

@ -302,7 +302,6 @@ pub fn StructDeserializer(struct_type: type) fn(allocator: std.mem.Allocator, *[
i8, u8, i16, u16, i32, u32,
i64, u64, i128, u128, i256, u256,
f32, f64 => {
//std.log.debug("field_type: {} (offset_mem.len: {})", .{field.type, offset_mem.len});
@field(ret, field.name) = std.mem.bytesAsValue(field.type, offset_mem[0..@sizeOf(field.type)]).*;
offset_mem = offset_mem[@sizeOf(field.type)..];
},
@ -341,21 +340,47 @@ pub fn Iter(struct_type: type) type {
return struct {
allocator: std.mem.Allocator,
handle: spacetime.RowIter,
buffer: [0x5_000]u8 = undefined,
contents: ?[]u8 = null,
buffer: []u8,
contents: []u8,
last_ret: SpacetimeValue = .OK,
inited: bool = false,
pub fn init(allocator: std.mem.Allocator, rowIter: spacetime.RowIter) !@This() {
const buffer = try allocator.alloc(u8, 0x20_000);
return .{
.allocator = allocator,
.handle = rowIter,
.buffer = buffer,
.contents = buffer[0..0],
.inited = true,
};
}
pub fn next(self: *@This()) spacetime.ReducerError!?struct_type {
var buffer_len: usize = undefined;
var ret: spacetime.SpacetimeValue = self.last_ret;
if(self.contents == null or self.contents.?.len == 0) {
blk: while(true) {
if(self.contents.len == 0) {
if(self.handle._inner == spacetime.RowIter.INVALID._inner) {
self.contents = null;
return null;
}
buffer_len = self.buffer.len;
ret = try spacetime.retMap(spacetime.row_iter_bsatn_advance(self.handle, @constCast(@ptrCast(&self.buffer)), &buffer_len));
ret = spacetime.retMap(spacetime.row_iter_bsatn_advance(self.handle, self.buffer.ptr, &buffer_len)) catch |err| {
switch(err) {
SpacetimeError.BUFFER_TOO_SMALL => {
self.buffer = try self.allocator.realloc(self.buffer, buffer_len);
continue :blk;
},
SpacetimeError.NO_SUCH_ITER => {
return SpacetimeError.NO_SUCH_ITER;
},
else => {
return SpacetimeError.UNKNOWN;
}
}
};
self.contents = self.buffer[0..buffer_len];
if(ret == .EXHAUSTED) {
@ -363,22 +388,26 @@ pub fn Iter(struct_type: type) type {
}
self.last_ret = ret;
}
if(self.contents == null or self.contents.?.len == 0) {
if(self.contents.len == 0) {
return null;
}
return try StructDeserializer(struct_type)(self.allocator, &(self.contents.?));
}
var offset = self.contents;
const retValue = try StructDeserializer(struct_type)(self.allocator, &offset);
self.contents = offset;
pub fn one_or_null(self: *@This()) ?struct_type {
defer self.close();
return self.next() catch null;
return retValue;
}
}
pub fn close(self: *@This()) void {
if (self.handle.invalid())
{
_ = spacetime.row_iter_bsatn_close(self.handle);
self.handle = spacetime.RowIter.INVALID;
self.contents = null;
}
self.contents = undefined;
self.allocator.free(self.buffer);
}
};
}
@ -429,9 +458,9 @@ pub fn Column2ORM(comptime table_name: []const u8, comptime column_name: [:0]con
};
const size: usize = getStructSize(nVal);
const mem = try self.allocator.alloc(u8, size);
var offset_mem = mem;
const mem = try self.allocator.alignedAlloc(u8, 1, size);
defer self.allocator.free(mem);
var offset_mem = mem;
getStructData(nVal, &offset_mem);
const data = mem[0..size];
@ -449,15 +478,12 @@ pub fn Column2ORM(comptime table_name: []const u8, comptime column_name: [:0]con
&rowIter
));
return Iter(struct_type){
.allocator = self.allocator,
.handle = rowIter,
};
return Iter(struct_type).init(self.allocator, rowIter);
}
pub fn find(self: @This(), val: wrapped_type) !?struct_type {
var iter = try self.filter(val);
return iter.one_or_null();
return try iter.next();
}
pub fn delete(self: @This(), val: wrapped_type) !void {
@ -472,8 +498,8 @@ pub fn Column2ORM(comptime table_name: []const u8, comptime column_name: [:0]con
const size: usize = getStructSize(nVal);
const mem = try self.allocator.alloc(u8, size);
var offset_mem = mem;
defer self.allocator.free(mem);
var offset_mem = mem;
getStructData(nVal, &offset_mem);
const data = mem[0..size];
@ -502,8 +528,8 @@ pub fn Column2ORM(comptime table_name: []const u8, comptime column_name: [:0]con
const size: usize = getStructSize(val);
const mem = try self.allocator.alloc(u8, size);
var offset_mem = mem;
defer self.allocator.free(mem);
var offset_mem = mem;
getStructData(val, &offset_mem);
const data = mem[0..size];
@ -579,15 +605,12 @@ pub fn Table2ORM(comptime table_name: []const u8) type {
return data_copy;
}
pub fn iter(self: @This()) Iter(struct_type) {
pub fn iter(self: @This()) !Iter(struct_type) {
var id: TableId = undefined;
_ = spacetime.table_id_from_name(table_name.ptr, table_name.len, &id);
var rowIter: spacetime.RowIter = undefined;
_ = spacetime.datastore_table_scan_bsatn(id, &rowIter);
return Iter(struct_type){
.allocator = self.allocator,
.handle = rowIter,
};
return Iter(struct_type).init(self.allocator, rowIter);
}
pub fn col(self: @This(), comptime column_name: [:0]const u8) Column2ORM(table_name, column_name) {
@ -609,15 +632,17 @@ pub fn Table2ORM(comptime table_name: []const u8) type {
pub const Local = struct {
allocator: std.mem.Allocator,
frame_allocator: std.mem.Allocator,
pub fn get(self: @This(), comptime table: []const u8) Table2ORM(table) {
return .{
.allocator = self.allocator,
.allocator = self.frame_allocator,
};
}
};
pub const ReducerContext = struct {
allocator: std.mem.Allocator,
sender: spacetime.Identity,
timestamp: spacetime.Timestamp,
connection_id: spacetime.ConnectionId,
@ -648,7 +673,7 @@ pub const RawMiscModuleExportV9 = enum {
RESERVED,
};
pub const RawSql = []u8;
pub const RawSql = Str;
pub const RawRowLevelSecurityDefV9 = struct {
sql: RawSql,