Skip to content

Commit

Permalink
Playtime: Add support for adding license
Browse files Browse the repository at this point in the history
  • Loading branch information
helgoboss committed Mar 2, 2024
1 parent 8c90c29 commit 970b436
Show file tree
Hide file tree
Showing 17 changed files with 244 additions and 32 deletions.
8 changes: 4 additions & 4 deletions CONTRIBUTING.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ See link:ARCHITECTURE.adoc[here].
|`/macros` |Various Rust macros for usage in this project only
|`/main` |Main crate: The actual ReaLearn instrument plug-in (`realearn`)
|`/playtime-api` |Playtime data structures for describing e.g. clip engine presets
|`/playtime-clip-engine` |Playtime Clip Engine for playing/recording clips (currently a private submodule)
|`/playtime-clip-engine` |Playtime Clip Engine for playing/recording clips (currently a private submodule). Is a workspace member because that makes it much simpler to use the same dependency versions everywhere.
|`/playtime-clip-engine-placeholder` |A placeholder crate for the Playtime Clip Engine. Users who don't have access to
the Playtime Clip Engine submodule, must rename this directory to `playtime-clip-engine` in order to be able to build
ReaLearn without feature `playtime`.
Expand Down Expand Up @@ -124,7 +124,7 @@ git clone https://github.com/helgoboss/realearn.git`
cd realearn
# ONLY For users who DON'T HAVE access to the private submodules
git submodule update --init main/lib/WDL main/lib/helgoboss-learn
git submodule update --init main/lib/WDL main/lib/helgoboss-learn helgoboss-license-api
rmdir playtime-clip-engine
mv playtime-clip-engine-placeholder playtime-clip-engine
Expand Down Expand Up @@ -162,7 +162,7 @@ git clone https://github.com/helgoboss/realearn.git
cd realearn
# ONLY For users who DON'T HAVE access to the private submodules
git submodule update --init main/lib/WDL main/lib/helgoboss-learn
git submodule update --init main/lib/WDL main/lib/helgoboss-learn helgoboss-license-api
rmdir playtime-clip-engine
mv playtime-clip-engine-placeholder playtime-clip-engine
Expand Down Expand Up @@ -210,7 +210,7 @@ git clone https://github.com/helgoboss/realearn.git
cd realearn
# ONLY For users who DON'T HAVE access to the private submodules
git submodule update --init main/lib/WDL main/lib/helgoboss-learn
git submodule update --init main/lib/WDL main/lib/helgoboss-learn helgoboss-license-api
rmdir playtime-clip-engine
mv playtime-clip-engine-placeholder playtime-clip-engine
Expand Down
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions api/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ strum.workspace = true
num_enum.workspace = true
enum-map.workspace = true
enumset = { workspace = true, features = ["serde", "alloc"]}
helgoboss-license-api.workspace = true

[dev-dependencies]
# For testing Lua compatibility
Expand Down
2 changes: 1 addition & 1 deletion api/src/runtime/info_event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ use serde::{Deserialize, Serialize};
#[derive(Clone, Eq, PartialEq, Debug, Serialize, Deserialize)]
#[serde(tag = "kind")]
pub enum InfoEvent {
/// Contains controller ID
AutoAddedController(AutoAddedControllerEvent),
PlaytimeActivatedSuccessfully,
}

#[derive(Clone, Eq, PartialEq, Debug, Serialize, Deserialize)]
Expand Down
13 changes: 13 additions & 0 deletions api/src/runtime/licensing.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
use helgoboss_license_api::persistence::LicenseData;
use serde::Serialize;

#[derive(Clone, Eq, PartialEq, Debug, Serialize)]
pub struct LicenseInfo {
pub licenses: Vec<ValidatedLicense>,
}

#[derive(Clone, Eq, PartialEq, Debug, Serialize)]
pub struct ValidatedLicense {
pub license: LicenseData,
pub valid: bool,
}
3 changes: 3 additions & 0 deletions api/src/runtime/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,6 @@ pub use info_event::*;

mod reaper;
pub use reaper::*;

mod licensing;
pub use licensing::*;
34 changes: 28 additions & 6 deletions main/src/infrastructure/data/license_management.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,23 @@ use anyhow::{anyhow, Context};
use helgoboss_license_api::persistence::{LicenseData, LicenseKey};
use helgoboss_license_api::runtime::License;
use serde::{Deserialize, Serialize};
use std::cell::RefCell;
use std::fmt::Debug;
use std::fs;
use std::path::PathBuf;
use std::rc::Rc;

pub type SharedLicenseManager = Rc<RefCell<LicenseManager>>;

pub trait LicenseManagerEventHandler: Debug {
fn licenses_changed(&self, source: &LicenseManager);
}

#[derive(Debug)]
pub struct LicenseManager {
licensing_file_path: PathBuf,
licensing: Licensing,
event_handler: Box<dyn LicenseManagerEventHandler>,
}

#[derive(Debug, Default, Serialize, Deserialize)]
Expand All @@ -25,15 +35,32 @@ impl LicenseManager {
/// Creates a manager using the given licensing file.
///
/// Immediately loads the licenses from the licensing file if it exists.
pub fn new(licensing_file_path: PathBuf) -> Self {
pub fn new(
licensing_file_path: PathBuf,
event_handler: Box<dyn LicenseManagerEventHandler>,
) -> Self {
let mut manager = Self {
licensing_file_path,
licensing: Default::default(),
event_handler,
};
let _ = manager.load();
manager
}

pub fn licenses(&self) -> &[License] {
&self.licensing.licenses
}

pub fn add_license(&mut self, key: LicenseKey) -> anyhow::Result<()> {
let license_data = LicenseData::try_from_key(&key)?;
let license = License::try_from(license_data)?;
self.licensing.licenses.push(license);
self.save()?;
self.event_handler.licenses_changed(self);
Ok(())
}

fn load(&mut self) -> anyhow::Result<()> {
let json = fs::read_to_string(&self.licensing_file_path)
.with_context(|| "couldn't read licensing file")?;
Expand All @@ -43,7 +70,6 @@ impl LicenseManager {
Ok(())
}

#[allow(dead_code)]
fn save(&mut self) -> anyhow::Result<()> {
let data: LicensingData = self.licensing.clone().into();
let json = serde_json::to_string_pretty(&data)
Expand All @@ -58,10 +84,6 @@ impl LicenseManager {
.with_context(|| "couldn't write licensing file")?;
Ok(())
}

pub fn licenses(&self) -> impl Iterator<Item = &License> + ExactSizeIterator {
self.licensing.licenses.iter()
}
}

impl From<LicensingData> for Licensing {
Expand Down
2 changes: 0 additions & 2 deletions main/src/infrastructure/data/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,7 @@ pub use osc_device_management::*;
mod virtual_control;
pub use virtual_control::*;

#[allow(unused)]
mod license_management;
#[allow(unused)]
pub use license_management::*;

mod common;
Expand Down
46 changes: 38 additions & 8 deletions main/src/infrastructure/plugin/backbone_shell.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,9 @@ use crate::domain::{
use crate::infrastructure::data::{
CommonCompartmentPresetManager, CompartmentPresetManagerEventHandler, ControllerManager,
ControllerManagerEventHandler, FileBasedControllerPresetManager, FileBasedMainPresetManager,
FileBasedPresetLinkManager, MainPresetSelectionConditions, OscDevice, OscDeviceManager,
SharedControllerManager, SharedControllerPresetManager, SharedMainPresetManager,
FileBasedPresetLinkManager, LicenseManager, LicenseManagerEventHandler,
MainPresetSelectionConditions, OscDevice, OscDeviceManager, SharedControllerManager,
SharedControllerPresetManager, SharedLicenseManager, SharedMainPresetManager,
SharedOscDeviceManager, SharedPresetLinkManager,
};
use crate::infrastructure::server;
Expand Down Expand Up @@ -161,6 +162,7 @@ pub struct BackboneShell {
/// RAII
_metrics_hook: Option<MetricsHook>,
state: RefCell<AppState>,
license_manager: SharedLicenseManager,
controller_preset_manager: SharedControllerPresetManager,
main_preset_manager: SharedMainPresetManager,
preset_link_manager: SharedPresetLinkManager,
Expand Down Expand Up @@ -337,9 +339,14 @@ impl BackboneShell {
.expect("couldn't get IsInRealTimeAudio function from REAPER"),
),
);
// License management
let license_manager = LicenseManager::new(
BackboneShell::helgoboss_resource_dir_path().join("licensing.json"),
Box::new(BackboneLicenseManagerEventHandler),
);
// This just initializes the clip engine, it doesn't add any clip matrix yet, so resource consumption is low.
#[cfg(feature = "playtime")]
Self::init_clip_engine();
Self::init_clip_engine(&license_manager);
// This is the backbone representation in the domain layer, of course we need this now already.
let backbone_state = Backbone::new(
additional_feedback_event_sender.clone(),
Expand Down Expand Up @@ -437,6 +444,7 @@ impl BackboneShell {
_tracing_hook: tracing_hook,
_metrics_hook: metrics_hook,
state: RefCell::new(AppState::Sleeping(sleeping_state)),
license_manager: Rc::new(RefCell::new(license_manager)),
controller_preset_manager: Rc::new(RefCell::new(controller_preset_manager)),
main_preset_manager: Rc::new(RefCell::new(main_preset_manager)),
preset_link_manager: Rc::new(RefCell::new(preset_link_manager)),
Expand Down Expand Up @@ -532,11 +540,8 @@ impl BackboneShell {
}

#[cfg(feature = "playtime")]
fn init_clip_engine() {
fn init_clip_engine(license_manager: &LicenseManager) {
use playtime_clip_engine::ClipEngine;
let license_manager = crate::infrastructure::data::LicenseManager::new(
BackboneShell::helgoboss_resource_dir_path().join("licensing.json"),
);
#[derive(Debug)]
struct RealearnMetricsRecorder;
impl playtime_clip_engine::MetricsRecorder for RealearnMetricsRecorder {
Expand Down Expand Up @@ -962,6 +967,10 @@ impl BackboneShell {
Ok(f(&state.async_runtime))
}

pub fn license_manager(&self) -> &SharedLicenseManager {
&self.license_manager
}

pub fn controller_preset_manager(&self) -> &SharedControllerPresetManager {
&self.controller_preset_manager
}
Expand Down Expand Up @@ -2534,6 +2543,27 @@ impl ControllerManagerEventHandler for BackboneControllerManagerEventHandler {
}
}

#[derive(Debug)]
pub struct BackboneLicenseManagerEventHandler;

impl LicenseManagerEventHandler for BackboneLicenseManagerEventHandler {
fn licenses_changed(&self, source: &LicenseManager) {
#[cfg(feature = "playtime")]
{
let success =
playtime_clip_engine::ClipEngine::get().handle_changed_licenses(source.licenses());
if success {
BackboneShell::get()
.proto_hub()
.notify_about_global_info_event(InfoEvent::PlaytimeActivatedSuccessfully);
}
}
BackboneShell::get()
.proto_hub()
.notify_licenses_changed(source);
}
}

#[derive(Debug)]
pub struct BackboneControllerPresetManagerEventHandler;

Expand Down Expand Up @@ -2739,7 +2769,7 @@ async fn maybe_create_controller_for_device(
.save_controller(controller)?;
BackboneShell::get()
.proto_hub()
.notify_about_info_event(InfoEvent::AutoAddedController(AutoAddedControllerEvent {
.notify_about_global_info_event(InfoEvent::AutoAddedController(AutoAddedControllerEvent {
controller_id: outcome.id,
}));
Ok(())
Expand Down
48 changes: 46 additions & 2 deletions main/src/infrastructure/proto/ext.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
use helgoboss_license_api::persistence::LicenseData;
use helgoboss_license_api::runtime::License;
use reaper_high::Reaper;
use reaper_medium::ReaperString;

use crate::infrastructure::data::{
ControllerManager, FileBasedControllerPresetManager, FileBasedMainPresetManager,
ControllerManager, FileBasedControllerPresetManager, FileBasedMainPresetManager, LicenseManager,
};
use crate::infrastructure::plugin::InstanceShell;

Expand All @@ -19,7 +21,7 @@ use crate::infrastructure::proto::{
QualifiedOccasionalColumnUpdate, QualifiedOccasionalRowUpdate, QualifiedOccasionalSlotUpdate,
QualifiedOccasionalTrackUpdate, SlotAddress,
};
use realearn_api::runtime::{ControllerPreset, MainPreset};
use realearn_api::runtime::{ControllerPreset, LicenseInfo, MainPreset, ValidatedLicense};

impl occasional_instance_update::Update {
pub fn settings(instance_shell: &InstanceShell) -> Self {
Expand Down Expand Up @@ -94,6 +96,48 @@ impl occasional_global_update::Update {
.expect("couldn't represent controller config as JSON");
Self::ControllerConfig(json)
}

pub fn playtime_is_licensed() -> Self {
let value = {
#[cfg(feature = "playtime")]
{
playtime_clip_engine::ClipEngine::get().has_valid_license()
}
#[cfg(not(feature = "playtime"))]
{
false
}
};
Self::PlaytimeIsLicensed(value)
}

pub fn license_info(license_manager: &LicenseManager) -> Self {
let license_info = LicenseInfo {
licenses: license_manager
.licenses()
.iter()
.map(|license| {
let valid = {
#[cfg(not(feature = "playtime"))]
{
false
}
#[cfg(feature = "playtime")]
{
playtime_clip_engine::ClipEngine::validate_license(license).is_ok()
}
};
ValidatedLicense {
license: license.clone().into(),
valid,
}
})
.collect(),
};
let json =
serde_json::to_string(&license_info).expect("couldn't represent license info as JSON");
Self::LicenseInfo(json)
}
}

impl MidiInputDevices {
Expand Down
Loading

0 comments on commit 970b436

Please sign in to comment.