Skip to content

Commit

Permalink
Implement the Server Protocol (#819)
Browse files Browse the repository at this point in the history
This implements a new improved version of the server protocol. The
following changes have been made:
- The protocol is based on JSON messages. This allows for example for
  more structured commands where it's easier to provide multiple 
  arguments for a command and even have optional arguments.
- For each command, there is a corresponding response. It is either a
  `success` response with possibly the value that you requested, or an
  `error` response with an error `code`.
- On top of the responses you also get sent `event` messages that
  indicate changes to the timer. These can either be changes triggered
  via a command that you sent or by changes that happened through other
  sources, such as the user directly interacting with the timer or an
  auto splitter.

The protocol is still work in progress and we will evolve it into a
protocol that fully allows synchronizing timers over the network.

The event sink has now been renamed to command sink, because there is
now a clear distinction between incoming commands and events that are
the results of these commands.
  • Loading branch information
CryZe authored Jun 22, 2024
1 parent 4ebb4bb commit 922cc84
Show file tree
Hide file tree
Showing 41 changed files with 2,845 additions and 806 deletions.
12 changes: 6 additions & 6 deletions benches/balanced_pb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,16 @@ criterion_main!(benches);
criterion_group!(benches, fake_splits, actual_splits);

fn run_with_splits(timer: &mut Timer, splits: &[f64]) {
timer.start();
timer.initialize_game_time();
timer.pause_game_time();
timer.start().unwrap();
timer.initialize_game_time().unwrap();
timer.pause_game_time().unwrap();

for &split in splits {
timer.set_game_time(TimeSpan::from_seconds(split));
timer.split();
timer.set_game_time(TimeSpan::from_seconds(split)).unwrap();
timer.split().unwrap();
}

timer.reset(true);
timer.reset(true).unwrap();
}

fn fake_splits(c: &mut Criterion) {
Expand Down
4 changes: 2 additions & 2 deletions benches/layout_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ fn artificial() -> (Timer, Layout, ImageCache) {
run.push_segment(Segment::new("Foo"));

let mut timer = Timer::new(run).unwrap();
timer.start();
timer.start().unwrap();

(timer, Layout::default_layout(), ImageCache::new())
}
Expand All @@ -28,7 +28,7 @@ fn real() -> (Timer, Layout, ImageCache) {
let run = livesplit::parse(&buf).unwrap();

let mut timer = Timer::new(run).unwrap();
timer.start();
timer.start().unwrap();

(timer, Layout::default_layout(), ImageCache::new())
}
Expand Down
14 changes: 7 additions & 7 deletions benches/scene_management.rs
Original file line number Diff line number Diff line change
Expand Up @@ -146,19 +146,19 @@ cfg_if::cfg_if! {

fn start_run(timer: &mut Timer) {
timer.set_current_timing_method(TimingMethod::GameTime);
timer.start();
timer.initialize_game_time();
timer.pause_game_time();
timer.set_game_time(TimeSpan::zero());
timer.start().unwrap();
timer.initialize_game_time().unwrap();
timer.pause_game_time().unwrap();
timer.set_game_time(TimeSpan::zero()).unwrap();
}

fn make_progress_run_with_splits_opt(timer: &mut Timer, splits: &[Option<f64>]) {
for &split in splits {
if let Some(split) = split {
timer.set_game_time(TimeSpan::from_seconds(split));
timer.split();
timer.set_game_time(TimeSpan::from_seconds(split)).unwrap();
timer.split().unwrap();
} else {
timer.skip_split();
timer.skip_split().unwrap();
}
}
}
Expand Down
14 changes: 7 additions & 7 deletions benches/software_rendering.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,19 +74,19 @@ cfg_if::cfg_if! {

fn start_run(timer: &mut Timer) {
timer.set_current_timing_method(TimingMethod::GameTime);
timer.start();
timer.initialize_game_time();
timer.pause_game_time();
timer.set_game_time(TimeSpan::zero());
timer.start().unwrap();
timer.initialize_game_time().unwrap();
timer.pause_game_time().unwrap();
timer.set_game_time(TimeSpan::zero()).unwrap();
}

fn make_progress_run_with_splits_opt(timer: &mut Timer, splits: &[Option<f64>]) {
for &split in splits {
if let Some(split) = split {
timer.set_game_time(TimeSpan::from_seconds(split));
timer.split();
timer.set_game_time(TimeSpan::from_seconds(split)).unwrap();
timer.split().unwrap();
} else {
timer.skip_split();
timer.skip_split().unwrap();
}
}
}
Expand Down
14 changes: 7 additions & 7 deletions benches/svg_rendering.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,19 +82,19 @@ cfg_if::cfg_if! {

fn start_run(timer: &mut Timer) {
timer.set_current_timing_method(TimingMethod::GameTime);
timer.start();
timer.initialize_game_time();
timer.pause_game_time();
timer.set_game_time(TimeSpan::zero());
timer.start().unwrap();
timer.initialize_game_time().unwrap();
timer.pause_game_time().unwrap();
timer.set_game_time(TimeSpan::zero()).unwrap();
}

fn make_progress_run_with_splits_opt(timer: &mut Timer, splits: &[Option<f64>]) {
for &split in splits {
if let Some(split) = split {
timer.set_game_time(TimeSpan::from_seconds(split));
timer.split();
timer.set_game_time(TimeSpan::from_seconds(split)).unwrap();
timer.split().unwrap();
} else {
timer.skip_split();
timer.skip_split().unwrap();
}
}
}
Expand Down
5 changes: 3 additions & 2 deletions capi/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
name = "livesplit-core-capi"
version = "0.11.0"
authors = ["Christopher Serr <[email protected]>"]
edition = "2018"
edition = "2021"

[lib]
name = "livesplit_core"
Expand All @@ -15,13 +15,14 @@ time = { version = "0.3.4", default-features = false, features = ["formatting"]
simdutf8 = { git = "https://github.com/CryZe/simdutf8", branch = "wasm-ub-panic", default-features = false }

wasm-bindgen = { version = "0.2.78", optional = true }
wasm-bindgen-futures = { version = "0.4.28", optional = true }
web-sys = { version = "0.3.28", optional = true }

[features]
default = ["image-shrinking"]
image-shrinking = ["livesplit-core/image-shrinking"]
software-rendering = ["livesplit-core/software-rendering"]
wasm-web = ["livesplit-core/wasm-web", "wasm-bindgen", "web-sys"]
wasm-web = ["livesplit-core/wasm-web", "wasm-bindgen", "wasm-bindgen-futures", "web-sys"]
auto-splitting = ["livesplit-core/auto-splitting"]
assume-str-parameters-are-utf8 = []
web-rendering = ["wasm-web", "livesplit-core/web-rendering"]
93 changes: 93 additions & 0 deletions capi/bind_gen/src/typescript.ts
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,99 @@ export enum TimerPhase {
Paused = 3,
}

/** An event informs you about a change in the timer. */
export enum Event {
/** The timer has been started. */
Started = 0,
/**
* A split happened. Note that the final split is signaled by `Finished`.
*/
Splitted = 1,
/**
* The final split happened, the run is now finished, but has not been reset
* yet.
*/
Finished = 2,
/** The timer has been reset. */
Reset = 3,
/** The previous split has been undone. */
SplitUndone = 4,
/** The current split has been skipped. */
SplitSkipped = 5,
/** The timer has been paused. */
Paused = 6,
/** The timer has been resumed. */
Resumed = 7,
/** All the pauses have been undone. */
PausesUndone = 8,
/** All the pauses have been undone and the timer has been resumed. */
PausesUndoneAndResumed = 9,
/** The comparison has been changed. */
ComparisonChanged = 10,
/** The timing method has been changed. */
TimingMethodChanged = 11,
/** The game time has been initialized. */
GameTimeInitialized = 12,
/** The game time has been set. */
GameTimeSet = 13,
/** The game time has been paused. */
GameTimePaused = 14,
/** The game time has been resumed. */
GameTimeResumed = 15,
/** The loading times have been set. */
LoadingTimesSet = 16,
/** A custom variable has been set. */
CustomVariableSet = 17,
}

/** An error that occurred when a command was being processed. */
export enum CommandError {
/** The operation is not supported. */
Unsupported = -1,
/** The timer can't be interacted with at the moment. */
Busy = -2,
/** There is already a run in progress. */
RunAlreadyInProgress = -3,
/** There is no run in progress. */
NoRunInProgress = -4,
/** The run is already finished. */
RunFinished = -5,
/** The time is negative, you can't split yet. */
NegativeTime = -6,
/** The last split can't be skipped. */
CantSkipLastSplit = -7,
/** There is no split to undo. */
CantUndoFirstSplit = -8,
/** The timer is already paused. */
AlreadyPaused = -9,
/** The timer is not paused. */
NotPaused = -10,
/** The requested comparison doesn't exist. */
ComparisonDoesntExist = -11,
/** The game time is already initialized. */
GameTimeAlreadyInitialized = -12,
/** The game time is already paused. */
GameTimeAlreadyPaused = -13,
/** The game time is not paused. */
GameTimeNotPaused = -14,
/** The time could not be parsed. */
CouldNotParseTime = -15,
/** The timer is currently paused. */
TimerPaused = -16,
/** The runner decided to not reset the run. */
RunnerDecidedAgainstReset = -17,
}

/** The result of a command that was processed. */
export type CommandResult = Event | CommandError;

/**
* Checks if the result of a command is a successful event instead of an error.
*/
export function isEvent(result: CommandResult): result is Event {
return result >= 0;
}

/** The state object describes the information to visualize for this component. */
export interface BlankSpaceComponentStateJson {
/** The background shown behind the component. */
Expand Down
Loading

0 comments on commit 922cc84

Please sign in to comment.