Basic Synced Object Test

This commit is contained in:
Elias Stepanik 2025-04-04 12:38:29 +02:00
parent 8f86a7997f
commit 86aa2c77be
14 changed files with 154 additions and 28 deletions

View File

@ -18,4 +18,5 @@ bevy_window = "0.15.0"
egui_dock = "0.14.0"
spacetimedb-sdk = "1.0"
hex = "0.4"
random_word = { version = "0.5.0", features = ["en"] }
random_word = { version = "0.5.0", features = ["en"] }
rand = "0.8.5"

View File

@ -28,7 +28,7 @@ impl Plugin for AppPlugin {
app.add_plugins(crate::plugins::camera::camera_plugin::CameraPlugin);
app.add_plugins(crate::plugins::ui::ui_plugin::UiPlugin);
app.add_plugins(crate::plugins::environment::environment_plugin::EnvironmentPlugin);
app.add_systems(Startup, setup_database);
app.add_plugins(crate::plugins::network::network_plugin::NetworkPlugin);
app.add_systems(Update, (debug_gizmos, toggle_ui_system));

View File

@ -4,8 +4,9 @@ use bevy::math::Vec3;
use bevy::prelude::*;
use bevy_render::camera::{Exposure, PhysicalCameraParameters, Projection};
use bevy_window::CursorGrabMode;
use rand::Rng;
use random_word::Lang;
use crate::module_bindings::set_name;
use crate::module_bindings::{set_name, spawn_entity, DbVector3};
use crate::plugins::network::systems::database::DbConnectionResource;
#[derive(Component)]
@ -47,6 +48,15 @@ pub fn setup(mut commands: Commands) {
));
}
fn random_vec3(min: f32, max: f32) -> Vec3 {
let mut rng = rand::thread_rng();
Vec3::new(
rng.gen_range(min..max),
rng.gen_range(min..max),
rng.gen_range(min..max),
)
}
/// Example system to control a camera using double-precision for position.
pub fn camera_controller_system(
time: Res<Time>,
@ -98,9 +108,17 @@ pub fn camera_controller_system(
let word = random_word::get(Lang::En);
if keyboard_input.just_pressed(KeyCode::KeyQ) {
if keyboard_input.just_pressed(KeyCode::Numpad1) {
ctx.0.reducers.set_name(word.to_string()).unwrap();
}
if keyboard_input.just_pressed(KeyCode::Numpad2) {
let rand_position = random_vec3(-10.0,10.0);
ctx.0.reducers.spawn_entity(DbVector3{
x: rand_position.x,
y: rand_position.y,
z: rand_position.z,
}).unwrap();
}
// ====================
// 3) Handle Keyboard Movement (WASD, Space, Shift)

View File

@ -7,7 +7,6 @@ pub struct EnvironmentPlugin;
impl Plugin for EnvironmentPlugin {
fn build(&self, app: &mut App) {
app.add_systems(Startup, init);
app.add_systems(FixedUpdate, fixed_update);
app.add_systems(Update, fixed_update);
app.add_systems(Update, sync_entities_system);
}
}

View File

@ -1,13 +1,93 @@
use bevy::prelude::*;
use spacetimedb_sdk::Table;
use crate::module_bindings::{entity_table, DbVector3, EntityTableAccess};
use crate::plugins::network::systems::database::DbConnectionResource;
#[derive(Component)]
pub struct EntityDto {
pub fn init(mut commands: Commands) {
pub entity_id: u32,
pub position: DbVector3,
}
pub fn fixed_update(time: Res<Time>,) {
pub fn init(mut commands: Commands,
ctx: Res<DbConnectionResource>,
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<StandardMaterial>>,) {
let debug_material = materials.add(StandardMaterial { ..default() });
for entity in ctx.0.db.entity().iter() {
commands.spawn((
Mesh3d(meshes.add(Cuboid::default()),),
MeshMaterial3d(debug_material.clone ()),
Transform::from_xyz(
entity.position.x,
entity.position.y,
entity.position.z,
),
EntityDto{
entity_id: entity.entity_id,
position: entity.position,
}
));
}
}
pub fn sync_entities_system(
mut commands: Commands,
db_resource: Res<DbConnectionResource>,
mut query: Query<(Entity, &mut Transform, &mut EntityDto)>,
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<StandardMaterial>>,
) {
let ctx = &db_resource.0.db;
// For each entity record in DB, see if it exists in ECS:
for db_entity in ctx.entity().iter() {
// Try to find a matching entity in ECS by comparing `entity_id`
if let Some((entity, mut transform, mut dto)) = query
.iter_mut()
.find(|(_, _, dto)| dto.entity_id == db_entity.entity_id)
{
// It already exists. Perhaps update ECS data to match DB:
dto.position = db_entity.position;
transform.translation = Vec3::new(
dto.position.x,
dto.position.y,
dto.position.z,
);
// ...do any other sync logic
} else {
let debug_material = materials.add(StandardMaterial { ..default() });
// Not found in ECS, so spawn a new entity
commands.spawn((
EntityDto {
entity_id: db_entity.entity_id,
position: db_entity.position.clone(),
},
// Create an initial transform using DB data
Transform::from_xyz(
db_entity.position.x,
db_entity.position.y,
db_entity.position.z,
),
GlobalTransform::default(),
Mesh3d(meshes.add(Cuboid::default()),),
MeshMaterial3d(debug_material.clone ()),
));
}
}
}
pub fn update(time: Res<Time>,) {

View File

@ -1,2 +1,2 @@
pub(crate) mod systems;
mod network_plugin;
pub mod network_plugin;

View File

@ -8,6 +8,6 @@ use crate::plugins::network::systems::database::setup_database;
pub struct NetworkPlugin;
impl Plugin for NetworkPlugin {
fn build(&self, app: &mut App) {
app.add_systems(Startup, setup_database);
app.add_systems(PreStartup, setup_database);
}
}

View File

@ -1,5 +1,5 @@
use bevy::prelude::{Commands, Resource};
use bevy::ecs::system::SystemState;
use bevy::prelude::{Commands, Resource, World};
use bevy::utils::info;
use spacetimedb_sdk::{credentials, DbContext, Error, Event, Identity, Status, Table, TableWithPrimaryKey};
use crate::module_bindings::*;
@ -23,6 +23,8 @@ pub fn setup_database(mut commands: Commands) {
subscribe_to_tables(&ctx);
ctx.run_threaded();
commands.insert_resource(DbConnectionResource(ctx));
}
@ -57,5 +59,5 @@ fn subscribe_to_tables(ctx: &DbConnection) {
ctx.subscription_builder()
.on_applied(on_sub_applied)
.on_error(on_sub_error)
.subscribe(["SELECT * FROM player"]);
.subscribe(["SELECT * FROM player", "SELECT * FROM entity"]);
}

View File

@ -10,4 +10,4 @@ crate-type = ["cdylib"]
[dependencies]
spacetimedb = "1.0.1"
log = "0.4"
log = "0.4"

View File

@ -44,4 +44,13 @@ pub fn identity_disconnected(ctx: &ReducerContext) {
// as it doesn't make sense for a client to disconnect without connecting first.
log::warn!("Disconnect event for unknown Player with identity {:?}", ctx.sender);
}
}
#[spacetimedb::reducer(init)]
pub fn init(ctx: &ReducerContext) -> Result<(), String> {
log::info!("Initializing...");
ctx.db.config().try_insert(Config {
id: 0,
})?;
Ok(())
}

View File

@ -0,0 +1,27 @@
use spacetimedb::{Identity, ReducerContext, Table};
use crate::types::vec3::{DbVector3};
#[spacetimedb::table(name = entity, public)]
#[derive(Debug, Clone, )]
pub struct Entity {
#[auto_inc]
#[primary_key]
pub entity_id: u32,
pub position: DbVector3,
}
#[spacetimedb::reducer]
pub fn spawn_entity(ctx: &ReducerContext, position: DbVector3) -> Result<(), String> {
ctx.db.entity().try_insert(Entity {
entity_id: 0,
position,
}).expect("TODO: panic message");
Ok(())
}

View File

@ -1,2 +1,3 @@
pub mod vec3;
pub mod player;
pub mod player;
mod entity;

View File

@ -29,3 +29,4 @@ fn validate_name(name: String) -> Result<String, String> {
Ok(name)
}
}

View File

@ -5,16 +5,4 @@ pub struct DbVector3 {
pub x: f32,
pub y: f32,
pub z: f32,
}
#[spacetimedb::table(name = entity, public)]
#[derive(Debug, Clone)]
pub struct Entity {
// The `auto_inc` attribute indicates to SpacetimeDB that
// this value should be determined by SpacetimeDB on insert.
#[auto_inc]
#[primary_key]
pub entity_id: u32,
pub position: DbVector3,
pub mass: u32,
}